summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-04-13 15:56:20 +0000
committerGerrit Code Review <review@openstack.org>2015-04-13 15:56:20 +0000
commit61475eb5b68ba86b2e88680ee36c37591e9e193a (patch)
treeac6efeee12139df080dcd8f8cc407777bf82f1bd
parent815e2c95acc402847bb497365ea5caba608fd8cc (diff)
parenta26824818fedec2ffdb102ff3ea08f6d81c5b54d (diff)
Merge "Remove Limited XML API Support from Manila"2015.1.0rc1
-rw-r--r--contrib/tempest/tempest/api/share/test_extensions.py2
-rw-r--r--manila/api/common.py76
-rw-r--r--manila/api/contrib/admin_actions.py1
-rw-r--r--manila/api/contrib/extended_quotas.py2
-rw-r--r--manila/api/contrib/quota_classes.py22
-rw-r--r--manila/api/contrib/quotas.py18
-rw-r--r--manila/api/contrib/services.py30
-rw-r--r--manila/api/contrib/share_actions.py39
-rw-r--r--manila/api/contrib/share_manage.py2
-rw-r--r--manila/api/contrib/share_type_access.py49
-rw-r--r--manila/api/contrib/share_unmanage.py2
-rw-r--r--manila/api/contrib/types_extra_specs.py1
-rw-r--r--manila/api/contrib/types_manage.py1
-rw-r--r--manila/api/contrib/used_limits.py1
-rw-r--r--manila/api/contrib/user_quotas.py2
-rw-r--r--manila/api/extensions.py58
-rw-r--r--manila/api/openstack/wsgi.py239
-rw-r--r--manila/api/schemas/atom-link.rng141
-rw-r--r--manila/api/schemas/v1.1/extension.rng11
-rw-r--r--manila/api/schemas/v1.1/extensions.rng6
-rw-r--r--manila/api/schemas/v1.1/limits.rng28
-rw-r--r--manila/api/schemas/v1.1/metadata.rng9
-rw-r--r--manila/api/urlmap.py10
-rw-r--r--manila/api/v1/limits.py30
-rw-r--r--manila/api/v1/security_service.py30
-rw-r--r--manila/api/v1/share_metadata.py9
-rw-r--r--manila/api/v1/share_networks.py45
-rw-r--r--manila/api/v1/share_servers.py34
-rw-r--r--manila/api/v1/share_snapshots.py45
-rw-r--r--manila/api/v1/shares.py32
-rw-r--r--manila/api/versions.py157
-rw-r--r--manila/api/xmlutil.py913
-rw-r--r--manila/tests/api/middleware/test_faults.py112
-rw-r--r--manila/tests/api/openstack/test_wsgi.py90
-rw-r--r--manila/tests/api/test_extensions.py57
-rw-r--r--manila/tests/api/test_xmlutil.py714
-rw-r--r--manila/tests/api/v1/test_limits.py109
-rw-r--r--manila/tests/test_utils.py30
-rw-r--r--manila/utils.py44
39 files changed, 21 insertions, 3180 deletions
diff --git a/contrib/tempest/tempest/api/share/test_extensions.py b/contrib/tempest/tempest/api/share/test_extensions.py
index c6d1712..4520755 100644
--- a/contrib/tempest/tempest/api/share/test_extensions.py
+++ b/contrib/tempest/tempest/api/share/test_extensions.py
@@ -27,5 +27,5 @@ class ExtensionsTest(base.BaseSharesTest):
27 27
28 # verify response 28 # verify response
29 self.assertIn(int(resp["status"]), self.HTTP_SUCCESS) 29 self.assertIn(int(resp["status"]), self.HTTP_SUCCESS)
30 keys = ["alias", "updated", "namespace", "name", "description"] 30 keys = ["alias", "updated", "name", "description"]
31 [self.assertIn(key, ext.keys()) for ext in extensions for key in keys] 31 [self.assertIn(key, ext.keys()) for ext in extensions for key in keys]
diff --git a/manila/api/common.py b/manila/api/common.py
index c3146bd..bff2544 100644
--- a/manila/api/common.py
+++ b/manila/api/common.py
@@ -22,10 +22,7 @@ import six
22from six.moves.urllib import parse 22from six.moves.urllib import parse
23import webob 23import webob
24 24
25from manila.api.openstack import wsgi
26from manila.api import xmlutil
27from manila.i18n import _ 25from manila.i18n import _
28from manila import utils
29 26
30api_common_opts = [ 27api_common_opts = [
31 cfg.IntOpt( 28 cfg.IntOpt(
@@ -41,7 +38,6 @@ api_common_opts = [
41CONF = cfg.CONF 38CONF = cfg.CONF
42CONF.register_opts(api_common_opts) 39CONF.register_opts(api_common_opts)
43LOG = log.getLogger(__name__) 40LOG = log.getLogger(__name__)
44XML_NS_V1 = 'http://docs.openstack.org/volume/api/v1'
45 41
46 42
47# Regex that matches alphanumeric characters, periods, hypens, 43# Regex that matches alphanumeric characters, periods, hypens,
@@ -267,78 +263,6 @@ class ViewBuilder(object):
267 return parse.urlunsplit(url_parts) 263 return parse.urlunsplit(url_parts)
268 264
269 265
270class MetadataDeserializer(wsgi.MetadataXMLDeserializer):
271 def deserialize(self, text):
272 dom = utils.safe_minidom_parse_string(text)
273 metadata_node = self.find_first_child_named(dom, "metadata")
274 metadata = self.extract_metadata(metadata_node)
275 return {'body': {'metadata': metadata}}
276
277
278class MetaItemDeserializer(wsgi.MetadataXMLDeserializer):
279 def deserialize(self, text):
280 dom = utils.safe_minidom_parse_string(text)
281 metadata_item = self.extract_metadata(dom)
282 return {'body': {'meta': metadata_item}}
283
284
285class MetadataXMLDeserializer(wsgi.XMLDeserializer):
286
287 def extract_metadata(self, metadata_node):
288 """Marshal the metadata attribute of a parsed request."""
289 if metadata_node is None:
290 return {}
291 metadata = {}
292 for meta_node in self.find_children_named(metadata_node, "meta"):
293 key = meta_node.getAttribute("key")
294 metadata[key] = self.extract_text(meta_node)
295 return metadata
296
297 def _extract_metadata_container(self, datastring):
298 dom = utils.safe_minidom_parse_string(datastring)
299 metadata_node = self.find_first_child_named(dom, "metadata")
300 metadata = self.extract_metadata(metadata_node)
301 return {'body': {'metadata': metadata}}
302
303 def create(self, datastring):
304 return self._extract_metadata_container(datastring)
305
306 def update_all(self, datastring):
307 return self._extract_metadata_container(datastring)
308
309 def update(self, datastring):
310 dom = utils.safe_minidom_parse_string(datastring)
311 metadata_item = self.extract_metadata(dom)
312 return {'body': {'meta': metadata_item}}
313
314
315metadata_nsmap = {None: xmlutil.XMLNS_V11}
316
317
318class MetaItemTemplate(xmlutil.TemplateBuilder):
319 def construct(self):
320 sel = xmlutil.Selector('meta', xmlutil.get_items, 0)
321 root = xmlutil.TemplateElement('meta', selector=sel)
322 root.set('key', 0)
323 root.text = 1
324 return xmlutil.MasterTemplate(root, 1, nsmap=metadata_nsmap)
325
326
327class MetadataTemplateElement(xmlutil.TemplateElement):
328 def will_render(self, datum):
329 return True
330
331
332class MetadataTemplate(xmlutil.TemplateBuilder):
333 def construct(self):
334 root = MetadataTemplateElement('metadata', selector='metadata')
335 elem = xmlutil.SubTemplateElement(root, 'meta',
336 selector=xmlutil.get_items)
337 elem.set('key', 0)
338 elem.text = 1
339 return xmlutil.MasterTemplate(root, 1, nsmap=metadata_nsmap)
340
341
342def remove_invalid_options(context, search_options, allowed_search_options): 266def remove_invalid_options(context, search_options, allowed_search_options):
343 """Remove search options that are not valid for non-admin API/context.""" 267 """Remove search options that are not valid for non-admin API/context."""
344 if context.is_admin: 268 if context.is_admin:
diff --git a/manila/api/contrib/admin_actions.py b/manila/api/contrib/admin_actions.py
index dd9f0d2..d42764b 100644
--- a/manila/api/contrib/admin_actions.py
+++ b/manila/api/contrib/admin_actions.py
@@ -131,7 +131,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
131 131
132 name = "AdminActions" 132 name = "AdminActions"
133 alias = "os-admin-actions" 133 alias = "os-admin-actions"
134 namespace = "http://docs.openstack.org/share/ext/admin-actions/api/v1.1"
135 updated = "2012-08-25T00:00:00+00:00" 134 updated = "2012-08-25T00:00:00+00:00"
136 135
137 def get_controller_extensions(self): 136 def get_controller_extensions(self):
diff --git a/manila/api/contrib/extended_quotas.py b/manila/api/contrib/extended_quotas.py
index 82efeee..b2c5e50 100644
--- a/manila/api/contrib/extended_quotas.py
+++ b/manila/api/contrib/extended_quotas.py
@@ -25,6 +25,4 @@ class Extended_quotas(extensions.ExtensionDescriptor):
25 25
26 name = "ExtendedQuotas" 26 name = "ExtendedQuotas"
27 alias = "os-extended-quotas" 27 alias = "os-extended-quotas"
28 namespace = ("http://docs.openstack.org/compute/ext/extended_quotas"
29 "/api/v1.1")
30 updated = "2013-06-09T00:00:00+00:00" 28 updated = "2013-06-09T00:00:00+00:00"
diff --git a/manila/api/contrib/quota_classes.py b/manila/api/contrib/quota_classes.py
index 9dd2567..05a4342 100644
--- a/manila/api/contrib/quota_classes.py
+++ b/manila/api/contrib/quota_classes.py
@@ -16,32 +16,14 @@
16import webob 16import webob
17 17
18from manila.api import extensions 18from manila.api import extensions
19from manila.api.openstack import wsgi
20from manila.api import xmlutil
21from manila import db 19from manila import db
22from manila import exception 20from manila import exception
23from manila import quota 21from manila import quota
24 22
25
26QUOTAS = quota.QUOTAS 23QUOTAS = quota.QUOTAS
27
28
29authorize = extensions.extension_authorizer('share', 'quota_classes') 24authorize = extensions.extension_authorizer('share', 'quota_classes')
30 25
31 26
32class QuotaClassTemplate(xmlutil.TemplateBuilder):
33 def construct(self):
34 root = xmlutil.TemplateElement('quota_class_set',
35 selector='quota_class_set')
36 root.set('id')
37
38 for resource in QUOTAS.resources:
39 elem = xmlutil.SubTemplateElement(root, resource)
40 elem.text = resource
41
42 return xmlutil.MasterTemplate(root, 1)
43
44
45class QuotaClassSetsController(object): 27class QuotaClassSetsController(object):
46 28
47 def _format_quota_set(self, quota_class, quota_set): 29 def _format_quota_set(self, quota_class, quota_set):
@@ -54,7 +36,6 @@ class QuotaClassSetsController(object):
54 36
55 return dict(quota_class_set=result) 37 return dict(quota_class_set=result)
56 38
57 @wsgi.serializers(xml=QuotaClassTemplate)
58 def show(self, req, id): 39 def show(self, req, id):
59 context = req.environ['manila.context'] 40 context = req.environ['manila.context']
60 authorize(context) 41 authorize(context)
@@ -66,7 +47,6 @@ class QuotaClassSetsController(object):
66 return self._format_quota_set(id, 47 return self._format_quota_set(id,
67 QUOTAS.get_class_quotas(context, id)) 48 QUOTAS.get_class_quotas(context, id))
68 49
69 @wsgi.serializers(xml=QuotaClassTemplate)
70 def update(self, req, id, body): 50 def update(self, req, id, body):
71 context = req.environ['manila.context'] 51 context = req.environ['manila.context']
72 authorize(context) 52 authorize(context)
@@ -89,8 +69,6 @@ class Quota_classes(extensions.ExtensionDescriptor):
89 69
90 name = "QuotaClasses" 70 name = "QuotaClasses"
91 alias = "os-quota-class-sets" 71 alias = "os-quota-class-sets"
92 namespace = ("http://docs.openstack.org/volume/ext/"
93 "quota-classes-sets/api/v1.1")
94 updated = "2012-03-12T00:00:00+00:00" 72 updated = "2012-03-12T00:00:00+00:00"
95 73
96 def get_resources(self): 74 def get_resources(self):
diff --git a/manila/api/contrib/quotas.py b/manila/api/contrib/quotas.py
index 594fa55..4c27f90 100644
--- a/manila/api/contrib/quotas.py
+++ b/manila/api/contrib/quotas.py
@@ -19,8 +19,6 @@ from six.moves.urllib import parse
19import webob 19import webob
20 20
21from manila.api import extensions 21from manila.api import extensions
22from manila.api.openstack import wsgi
23from manila.api import xmlutil
24from manila import db 22from manila import db
25from manila.db.sqlalchemy import api as sqlalchemy_api 23from manila.db.sqlalchemy import api as sqlalchemy_api
26from manila import exception 24from manila import exception
@@ -38,18 +36,6 @@ authorize_show = extensions.extension_authorizer('compute', 'quotas:show')
38authorize_delete = extensions.extension_authorizer('compute', 'quotas:delete') 36authorize_delete = extensions.extension_authorizer('compute', 'quotas:delete')
39 37
40 38
41class QuotaTemplate(xmlutil.TemplateBuilder):
42 def construct(self):
43 root = xmlutil.TemplateElement('quota_set', selector='quota_set')
44 root.set('id')
45
46 for resource in QUOTAS.resources:
47 elem = xmlutil.SubTemplateElement(root, resource)
48 elem.text = resource
49
50 return xmlutil.MasterTemplate(root, 1)
51
52
53class QuotaSetsController(object): 39class QuotaSetsController(object):
54 40
55 def __init__(self, ext_mgr): 41 def __init__(self, ext_mgr):
@@ -90,7 +76,6 @@ class QuotaSetsController(object):
90 else: 76 else:
91 return dict((k, v['limit']) for k, v in values.items()) 77 return dict((k, v['limit']) for k, v in values.items())
92 78
93 @wsgi.serializers(xml=QuotaTemplate)
94 def show(self, req, id): 79 def show(self, req, id):
95 context = req.environ['manila.context'] 80 context = req.environ['manila.context']
96 authorize_show(context) 81 authorize_show(context)
@@ -105,7 +90,6 @@ class QuotaSetsController(object):
105 except exception.NotAuthorized: 90 except exception.NotAuthorized:
106 raise webob.exc.HTTPForbidden() 91 raise webob.exc.HTTPForbidden()
107 92
108 @wsgi.serializers(xml=QuotaTemplate)
109 def update(self, req, id, body): 93 def update(self, req, id, body):
110 context = req.environ['manila.context'] 94 context = req.environ['manila.context']
111 authorize_update(context) 95 authorize_update(context)
@@ -209,7 +193,6 @@ class QuotaSetsController(object):
209 raise webob.exc.HTTPForbidden() 193 raise webob.exc.HTTPForbidden()
210 return {'quota_set': self._get_quotas(context, id, user_id=user_id)} 194 return {'quota_set': self._get_quotas(context, id, user_id=user_id)}
211 195
212 @wsgi.serializers(xml=QuotaTemplate)
213 def defaults(self, req, id): 196 def defaults(self, req, id):
214 context = req.environ['manila.context'] 197 context = req.environ['manila.context']
215 authorize_show(context) 198 authorize_show(context)
@@ -241,7 +224,6 @@ class Quotas(extensions.ExtensionDescriptor):
241 224
242 name = "Quotas" 225 name = "Quotas"
243 alias = "os-quota-sets" 226 alias = "os-quota-sets"
244 namespace = "http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1"
245 updated = "2011-08-08T00:00:00+00:00" 227 updated = "2011-08-08T00:00:00+00:00"
246 228
247 def get_resources(self): 229 def get_resources(self):
diff --git a/manila/api/contrib/services.py b/manila/api/contrib/services.py
index a28f9a4..4df0980 100644
--- a/manila/api/contrib/services.py
+++ b/manila/api/contrib/services.py
@@ -17,8 +17,6 @@ from oslo_log import log
17import webob.exc 17import webob.exc
18 18
19from manila.api import extensions 19from manila.api import extensions
20from manila.api.openstack import wsgi
21from manila.api import xmlutil
22from manila import db 20from manila import db
23from manila import exception 21from manila import exception
24from manila import utils 22from manila import utils
@@ -27,33 +25,7 @@ LOG = log.getLogger(__name__)
27authorize = extensions.extension_authorizer('share', 'services') 25authorize = extensions.extension_authorizer('share', 'services')
28 26
29 27
30class ServicesIndexTemplate(xmlutil.TemplateBuilder):
31 def construct(self):
32 root = xmlutil.TemplateElement('services')
33 elem = xmlutil.SubTemplateElement(root, 'service', selector='services')
34 elem.set('binary')
35 elem.set('host')
36 elem.set('zone')
37 elem.set('status')
38 elem.set('state')
39 elem.set('update_at')
40
41 return xmlutil.MasterTemplate(root, 1)
42
43
44class ServicesUpdateTemplate(xmlutil.TemplateBuilder):
45 def construct(self):
46 root = xmlutil.TemplateElement('host')
47 root.set('host')
48 root.set('disabled')
49 root.set('binary')
50 root.set('status')
51
52 return xmlutil.MasterTemplate(root, 1)
53
54
55class ServiceController(object): 28class ServiceController(object):
56 @wsgi.serializers(xml=ServicesIndexTemplate)
57 def index(self, req): 29 def index(self, req):
58 """Return a list of all running services.""" 30 """Return a list of all running services."""
59 context = req.environ['manila.context'] 31 context = req.environ['manila.context']
@@ -90,7 +62,6 @@ class ServiceController(object):
90 62
91 return {'services': services} 63 return {'services': services}
92 64
93 @wsgi.serializers(xml=ServicesUpdateTemplate)
94 def update(self, req, id, body): 65 def update(self, req, id, body):
95 """Enable/Disable scheduling for a service.""" 66 """Enable/Disable scheduling for a service."""
96 context = req.environ['manila.context'] 67 context = req.environ['manila.context']
@@ -126,7 +97,6 @@ class Services(extensions.ExtensionDescriptor):
126 97
127 name = "Services" 98 name = "Services"
128 alias = "os-services" 99 alias = "os-services"
129 namespace = "http://docs.openstack.org/volume/ext/services/api/v2"
130 updated = "2012-10-28T00:00:00-00:00" 100 updated = "2012-10-28T00:00:00-00:00"
131 101
132 def get_resources(self): 102 def get_resources(self):
diff --git a/manila/api/contrib/share_actions.py b/manila/api/contrib/share_actions.py
index d31eaa0..a625612 100644
--- a/manila/api/contrib/share_actions.py
+++ b/manila/api/contrib/share_actions.py
@@ -19,7 +19,6 @@ import webob
19 19
20from manila.api import extensions 20from manila.api import extensions
21from manila.api.openstack import wsgi 21from manila.api.openstack import wsgi
22from manila.api import xmlutil
23from manila import exception 22from manila import exception
24from manila.i18n import _ 23from manila.i18n import _
25from manila import share 24from manila import share
@@ -28,40 +27,6 @@ from manila import share
28authorize = extensions.extension_authorizer('share', 'services') 27authorize = extensions.extension_authorizer('share', 'services')
29 28
30 29
31class ShareAccessTemplate(xmlutil.TemplateBuilder):
32 """XML Template for share access management methods."""
33
34 def construct(self):
35 root = xmlutil.TemplateElement('access',
36 selector='access')
37 root.set("share_id")
38 root.set("deleted")
39 root.set("created_at")
40 root.set("updated_at")
41 root.set("access_type")
42 root.set("access_to")
43 root.set("state")
44 root.set("deleted_at")
45 root.set("id")
46
47 return xmlutil.MasterTemplate(root, 1)
48
49
50class ShareAccessListTemplate(xmlutil.TemplateBuilder):
51 """XML Template for share access list."""
52
53 def construct(self):
54 root = xmlutil.TemplateElement('access_list')
55 elem = xmlutil.SubTemplateElement(root, 'share',
56 selector='access_list')
57 elem.set("state")
58 elem.set("id")
59 elem.set("access_type")
60 elem.set("access_to")
61
62 return xmlutil.MasterTemplate(root, 1)
63
64
65class ShareActionsController(wsgi.Controller): 30class ShareActionsController(wsgi.Controller):
66 def __init__(self, *args, **kwargs): 31 def __init__(self, *args, **kwargs):
67 super(ShareActionsController, self).__init__(*args, **kwargs) 32 super(ShareActionsController, self).__init__(*args, **kwargs)
@@ -118,7 +83,6 @@ class ShareActionsController(wsgi.Controller):
118 raise webob.exc.HTTPBadRequest(explanation=exc_str) 83 raise webob.exc.HTTPBadRequest(explanation=exc_str)
119 84
120 @wsgi.action('os-allow_access') 85 @wsgi.action('os-allow_access')
121 @wsgi.serializers(xml=ShareAccessTemplate)
122 def _allow_access(self, req, id, body): 86 def _allow_access(self, req, id, body):
123 """Add share access rule.""" 87 """Add share access rule."""
124 context = req.environ['manila.context'] 88 context = req.environ['manila.context']
@@ -146,7 +110,6 @@ class ShareActionsController(wsgi.Controller):
146 return {'access': access} 110 return {'access': access}
147 111
148 @wsgi.action('os-deny_access') 112 @wsgi.action('os-deny_access')
149 @wsgi.serializers(xml=ShareAccessTemplate)
150 def _deny_access(self, req, id, body): 113 def _deny_access(self, req, id, body):
151 """Remove access rule.""" 114 """Remove access rule."""
152 context = req.environ['manila.context'] 115 context = req.environ['manila.context']
@@ -164,7 +127,6 @@ class ShareActionsController(wsgi.Controller):
164 return webob.Response(status_int=202) 127 return webob.Response(status_int=202)
165 128
166 @wsgi.action('os-access_list') 129 @wsgi.action('os-access_list')
167 @wsgi.serializers(xml=ShareAccessListTemplate)
168 def _access_list(self, req, id, body): 130 def _access_list(self, req, id, body):
169 """list access rules.""" 131 """list access rules."""
170 context = req.environ['manila.context'] 132 context = req.environ['manila.context']
@@ -183,7 +145,6 @@ class Share_actions(extensions.ExtensionDescriptor):
183 145
184 name = 'ShareActions' 146 name = 'ShareActions'
185 alias = 'share-actions' 147 alias = 'share-actions'
186 namespace = ''
187 updated = '2012-08-14T00:00:00+00:00' 148 updated = '2012-08-14T00:00:00+00:00'
188 149
189 def get_controller_extensions(self): 150 def get_controller_extensions(self):
diff --git a/manila/api/contrib/share_manage.py b/manila/api/contrib/share_manage.py
index 61aef4d..ebe3559 100644
--- a/manila/api/contrib/share_manage.py
+++ b/manila/api/contrib/share_manage.py
@@ -120,8 +120,6 @@ class Share_manage(extensions.ExtensionDescriptor):
120 120
121 name = 'ShareManage' 121 name = 'ShareManage'
122 alias = 'os-share-manage' 122 alias = 'os-share-manage'
123 namespace = ('http://docs.openstack.org/share/ext/'
124 'os-share-manage/api/v1')
125 updated = '2015-02-17T00:00:00+00:00' 123 updated = '2015-02-17T00:00:00+00:00'
126 124
127 def get_resources(self): 125 def get_resources(self):
diff --git a/manila/api/contrib/share_type_access.py b/manila/api/contrib/share_type_access.py
index 20e302b..2e54b98 100644
--- a/manila/api/contrib/share_type_access.py
+++ b/manila/api/contrib/share_type_access.py
@@ -20,7 +20,6 @@ import webob
20 20
21from manila.api import extensions 21from manila.api import extensions
22from manila.api.openstack import wsgi 22from manila.api.openstack import wsgi
23from manila.api import xmlutil
24from manila import exception 23from manila import exception
25from manila.i18n import _ 24from manila.i18n import _
26from manila.share import share_types 25from manila.share import share_types
@@ -31,45 +30,6 @@ soft_authorize = extensions.soft_extension_authorizer('share',
31authorize = extensions.extension_authorizer('share', 'share_type_access') 30authorize = extensions.extension_authorizer('share', 'share_type_access')
32 31
33 32
34def make_share_type(elem):
35 elem.set('{%s}is_public' % Share_type_access.namespace,
36 '%s:is_public' % Share_type_access.alias)
37
38
39def make_share_type_access(elem):
40 elem.set('share_type_id')
41 elem.set('project_id')
42
43
44class ShareTypeTemplate(xmlutil.TemplateBuilder):
45 def construct(self):
46 root = xmlutil.TemplateElement('share_type', selector='share_type')
47 make_share_type(root)
48 alias = Share_type_access.alias
49 namespace = Share_type_access.namespace
50 return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
51
52
53class ShareTypesTemplate(xmlutil.TemplateBuilder):
54 def construct(self):
55 root = xmlutil.TemplateElement('share_types')
56 elem = xmlutil.SubTemplateElement(
57 root, 'share_type', selector='share_types')
58 make_share_type(elem)
59 alias = Share_type_access.alias
60 namespace = Share_type_access.namespace
61 return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
62
63
64class ShareTypeAccessTemplate(xmlutil.TemplateBuilder):
65 def construct(self):
66 root = xmlutil.TemplateElement('share_type_access')
67 elem = xmlutil.SubTemplateElement(root, 'access',
68 selector='share_type_access')
69 make_share_type_access(elem)
70 return xmlutil.MasterTemplate(root, 1)
71
72
73def _marshall_share_type_access(share_type): 33def _marshall_share_type_access(share_type):
74 rval = [] 34 rval = []
75 for project_id in share_type['projects']: 35 for project_id in share_type['projects']:
@@ -82,7 +42,6 @@ def _marshall_share_type_access(share_type):
82class ShareTypeAccessController(object): 42class ShareTypeAccessController(object):
83 """The share type access API controller for the OpenStack API.""" 43 """The share type access API controller for the OpenStack API."""
84 44
85 @wsgi.serializers(xml=ShareTypeAccessTemplate)
86 def index(self, req, type_id): 45 def index(self, req, type_id):
87 context = req.environ['manila.context'] 46 context = req.environ['manila.context']
88 authorize(context) 47 authorize(context)
@@ -123,8 +82,6 @@ class ShareTypeActionController(wsgi.Controller):
123 def show(self, req, resp_obj, id): 82 def show(self, req, resp_obj, id):
124 context = req.environ['manila.context'] 83 context = req.environ['manila.context']
125 if soft_authorize(context): 84 if soft_authorize(context):
126 # Attach our slave template to the response object
127 resp_obj.attach(xml=ShareTypeTemplate())
128 share_type = req.get_db_share_type(id) 85 share_type = req.get_db_share_type(id)
129 self._extend_share_type(resp_obj.obj['share_type'], share_type) 86 self._extend_share_type(resp_obj.obj['share_type'], share_type)
130 87
@@ -132,8 +89,6 @@ class ShareTypeActionController(wsgi.Controller):
132 def index(self, req, resp_obj): 89 def index(self, req, resp_obj):
133 context = req.environ['manila.context'] 90 context = req.environ['manila.context']
134 if soft_authorize(context): 91 if soft_authorize(context):
135 # Attach our slave template to the response object
136 resp_obj.attach(xml=ShareTypesTemplate())
137 for share_type_rval in list(resp_obj.obj['share_types']): 92 for share_type_rval in list(resp_obj.obj['share_types']):
138 type_id = share_type_rval['id'] 93 type_id = share_type_rval['id']
139 share_type = req.get_db_share_type(type_id) 94 share_type = req.get_db_share_type(type_id)
@@ -143,8 +98,6 @@ class ShareTypeActionController(wsgi.Controller):
143 def create(self, req, body, resp_obj): 98 def create(self, req, body, resp_obj):
144 context = req.environ['manila.context'] 99 context = req.environ['manila.context']
145 if soft_authorize(context): 100 if soft_authorize(context):
146 # Attach our slave template to the response object
147 resp_obj.attach(xml=ShareTypeTemplate())
148 type_id = resp_obj.obj['share_type']['id'] 101 type_id = resp_obj.obj['share_type']['id']
149 share_type = req.get_db_share_type(type_id) 102 share_type = req.get_db_share_type(type_id)
150 self._extend_share_type(resp_obj.obj['share_type'], share_type) 103 self._extend_share_type(resp_obj.obj['share_type'], share_type)
@@ -193,8 +146,6 @@ class Share_type_access(extensions.ExtensionDescriptor):
193 146
194 name = "ShareTypeAccess" 147 name = "ShareTypeAccess"
195 alias = "os-share-type-access" 148 alias = "os-share-type-access"
196 namespace = ("http://docs.openstack.org/share/"
197 "ext/os-share-type-access/api/v1")
198 updated = "2015-03-02T00:00:00Z" 149 updated = "2015-03-02T00:00:00Z"
199 150
200 def get_resources(self): 151 def get_resources(self):
diff --git a/manila/api/contrib/share_unmanage.py b/manila/api/contrib/share_unmanage.py
index 98e3d61..420271d 100644
--- a/manila/api/contrib/share_unmanage.py
+++ b/manila/api/contrib/share_unmanage.py
@@ -78,8 +78,6 @@ class Share_unmanage(extensions.ExtensionDescriptor):
78 78
79 name = 'ShareUnmanage' 79 name = 'ShareUnmanage'
80 alias = 'os-share-unmanage' 80 alias = 'os-share-unmanage'
81 namespace = ('http://docs.openstack.org/share/ext/'
82 'os-share-unmanage/api/v1')
83 updated = '2015-02-17T00:00:00+00:00' 81 updated = '2015-02-17T00:00:00+00:00'
84 82
85 def get_resources(self): 83 def get_resources(self):
diff --git a/manila/api/contrib/types_extra_specs.py b/manila/api/contrib/types_extra_specs.py
index 8745247..3fafc91 100644
--- a/manila/api/contrib/types_extra_specs.py
+++ b/manila/api/contrib/types_extra_specs.py
@@ -162,7 +162,6 @@ class Types_extra_specs(extensions.ExtensionDescriptor):
162 162
163 name = "TypesExtraSpecs" 163 name = "TypesExtraSpecs"
164 alias = "os-types-extra-specs" 164 alias = "os-types-extra-specs"
165 namespace = "http://docs.openstack.org/share/ext/types-extra-specs/api/v1"
166 updated = "2011-08-24T00:00:00+00:00" 165 updated = "2011-08-24T00:00:00+00:00"
167 166
168 def get_resources(self): 167 def get_resources(self):
diff --git a/manila/api/contrib/types_manage.py b/manila/api/contrib/types_manage.py
index 973a6f0..7d2054f 100644
--- a/manila/api/contrib/types_manage.py
+++ b/manila/api/contrib/types_manage.py
@@ -124,7 +124,6 @@ class Types_manage(extensions.ExtensionDescriptor):
124 124
125 name = "TypesManage" 125 name = "TypesManage"
126 alias = "os-types-manage" 126 alias = "os-types-manage"
127 namespace = "http://docs.openstack.org/share/ext/types-manage/api/v1"
128 updated = "2011-08-24T00:00:00+00:00" 127 updated = "2011-08-24T00:00:00+00:00"
129 128
130 def get_controller_extensions(self): 129 def get_controller_extensions(self):
diff --git a/manila/api/contrib/used_limits.py b/manila/api/contrib/used_limits.py
index dbdad64..b1b0a75 100644
--- a/manila/api/contrib/used_limits.py
+++ b/manila/api/contrib/used_limits.py
@@ -55,7 +55,6 @@ class Used_limits(extensions.ExtensionDescriptor):
55 55
56 name = "UsedLimits" 56 name = "UsedLimits"
57 alias = 'os-used-limits' 57 alias = 'os-used-limits'
58 namespace = "http://docs.openstack.org/share/ext/used-limits/api/v1.0"
59 updated = "2014-03-27T00:00:00+00:00" 58 updated = "2014-03-27T00:00:00+00:00"
60 59
61 def get_controller_extensions(self): 60 def get_controller_extensions(self):
diff --git a/manila/api/contrib/user_quotas.py b/manila/api/contrib/user_quotas.py
index 2119fbe..224e125 100644
--- a/manila/api/contrib/user_quotas.py
+++ b/manila/api/contrib/user_quotas.py
@@ -22,6 +22,4 @@ class User_quotas(extensions.ExtensionDescriptor):
22 22
23 name = "UserQuotas" 23 name = "UserQuotas"
24 alias = "os-user-quotas" 24 alias = "os-user-quotas"
25 namespace = ("http://docs.openstack.org/compute/ext/user_quotas"
26 "/api/v1.1")
27 updated = "2013-07-18T00:00:00+00:00" 25 updated = "2013-07-18T00:00:00+00:00"
diff --git a/manila/api/extensions.py b/manila/api/extensions.py
index 1853f7b..62f85a7 100644
--- a/manila/api/extensions.py
+++ b/manila/api/extensions.py
@@ -25,7 +25,6 @@ import webob.exc
25 25
26import manila.api.openstack 26import manila.api.openstack
27from manila.api.openstack import wsgi 27from manila.api.openstack import wsgi
28from manila.api import xmlutil
29from manila import exception 28from manila import exception
30from manila.i18n import _LE 29from manila.i18n import _LE
31from manila.i18n import _LI 30from manila.i18n import _LI
@@ -52,10 +51,6 @@ class ExtensionDescriptor(object):
52 51
53 # Description comes from the docstring for the class 52 # Description comes from the docstring for the class
54 53
55 # The XML namespace for the extension, e.g.,
56 # 'http://www.fox.in.socks/api/ext/pie/v1.0'
57 namespace = None
58
59 # The timestamp when the extension was last updated, e.g., 54 # The timestamp when the extension was last updated, e.g.,
60 # '2011-01-22T13:25:27-06:00' 55 # '2011-01-22T13:25:27-06:00'
61 updated = None 56 updated = None
@@ -83,55 +78,6 @@ class ExtensionDescriptor(object):
83 controller_exts = [] 78 controller_exts = []
84 return controller_exts 79 return controller_exts
85 80
86 @classmethod
87 def nsmap(cls):
88 """Synthesize a namespace map from extension."""
89
90 # Start with a base nsmap
91 nsmap = ext_nsmap.copy()
92
93 # Add the namespace for the extension
94 nsmap[cls.alias] = cls.namespace
95
96 return nsmap
97
98 @classmethod
99 def xmlname(cls, name):
100 """Synthesize element and attribute names."""
101
102 return '{%s}%s' % (cls.namespace, name)
103
104
105def make_ext(elem):
106 elem.set('name')
107 elem.set('namespace')
108 elem.set('alias')
109 elem.set('updated')
110
111 desc = xmlutil.SubTemplateElement(elem, 'description')
112 desc.text = 'description'
113
114 xmlutil.make_links(elem, 'links')
115
116
117ext_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
118
119
120class ExtensionTemplate(xmlutil.TemplateBuilder):
121 def construct(self):
122 root = xmlutil.TemplateElement('extension', selector='extension')
123 make_ext(root)
124 return xmlutil.MasterTemplate(root, 1, nsmap=ext_nsmap)
125
126
127class ExtensionsTemplate(xmlutil.TemplateBuilder):
128 def construct(self):
129 root = xmlutil.TemplateElement('extensions')
130 elem = xmlutil.SubTemplateElement(root, 'extension',
131 selector='extensions')
132 make_ext(elem)
133 return xmlutil.MasterTemplate(root, 1, nsmap=ext_nsmap)
134
135 81
136class ExtensionsResource(wsgi.Resource): 82class ExtensionsResource(wsgi.Resource):
137 83
@@ -144,19 +90,16 @@ class ExtensionsResource(wsgi.Resource):
144 ext_data['name'] = ext.name 90 ext_data['name'] = ext.name
145 ext_data['alias'] = ext.alias 91 ext_data['alias'] = ext.alias
146 ext_data['description'] = ext.__doc__ 92 ext_data['description'] = ext.__doc__
147 ext_data['namespace'] = ext.namespace
148 ext_data['updated'] = ext.updated 93 ext_data['updated'] = ext.updated
149 ext_data['links'] = [] # TODO(dprince): implement extension links 94 ext_data['links'] = [] # TODO(dprince): implement extension links
150 return ext_data 95 return ext_data
151 96
152 @wsgi.serializers(xml=ExtensionsTemplate)
153 def index(self, req): 97 def index(self, req):
154 extensions = [] 98 extensions = []
155 for _alias, ext in six.iteritems(self.extension_manager.extensions): 99 for _alias, ext in six.iteritems(self.extension_manager.extensions):
156 extensions.append(self._translate(ext)) 100 extensions.append(self._translate(ext))
157 return dict(extensions=extensions) 101 return dict(extensions=extensions)
158 102
159 @wsgi.serializers(xml=ExtensionTemplate)
160 def show(self, req, id): 103 def show(self, req, id):
161 try: 104 try:
162 # NOTE(dprince): the extensions alias is used as the 'id' for show 105 # NOTE(dprince): the extensions alias is used as the 'id' for show
@@ -240,7 +183,6 @@ class ExtensionManager(object):
240 LOG.debug('Ext alias: %s', extension.alias) 183 LOG.debug('Ext alias: %s', extension.alias)
241 LOG.debug('Ext description: %s', 184 LOG.debug('Ext description: %s',
242 ' '.join(extension.__doc__.strip().split())) 185 ' '.join(extension.__doc__.strip().split()))
243 LOG.debug('Ext namespace: %s', extension.namespace)
244 LOG.debug('Ext updated: %s', extension.updated) 186 LOG.debug('Ext updated: %s', extension.updated)
245 except AttributeError as ex: 187 except AttributeError as ex:
246 LOG.exception(_LE("Exception loading extension: %s"), 188 LOG.exception(_LE("Exception loading extension: %s"),
diff --git a/manila/api/openstack/wsgi.py b/manila/api/openstack/wsgi.py
index 01c9ad3..be88a90 100644
--- a/manila/api/openstack/wsgi.py
+++ b/manila/api/openstack/wsgi.py
@@ -28,37 +28,14 @@ from manila.i18n import _LI
28from manila import utils 28from manila import utils
29from manila import wsgi 29from manila import wsgi
30 30
31from lxml import etree
32from xml.dom import minidom
33from xml.parsers import expat
34
35
36XMLNS_V1 = 'http://docs.openstack.org/volume/api/v1'
37XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
38
39LOG = log.getLogger(__name__) 31LOG = log.getLogger(__name__)
40 32
41# The vendor content types should serialize identically to the non-vendor
42# content types. So to avoid littering the code with both options, we
43# map the vendor to the other when looking up the type
44_CONTENT_TYPE_MAP = {
45 'application/vnd.openstack.volume+json': 'application/json',
46 'application/vnd.openstack.volume+xml': 'application/xml',
47}
48
49SUPPORTED_CONTENT_TYPES = ( 33SUPPORTED_CONTENT_TYPES = (
50 'application/json', 34 'application/json',
51 'application/vnd.openstack.volume+json',
52 'application/xml',
53 'application/vnd.openstack.volume+xml',
54) 35)
55 36
56_MEDIA_TYPE_MAP = { 37_MEDIA_TYPE_MAP = {
57 'application/vnd.openstack.volume+json': 'json',
58 'application/json': 'json', 38 'application/json': 'json',
59 'application/vnd.openstack.volume+xml': 'xml',
60 'application/xml': 'xml',
61 'application/atom+xml': 'atom',
62} 39}
63 40
64 41
@@ -252,95 +229,6 @@ class JSONDeserializer(TextDeserializer):
252 return {'body': self._from_json(datastring)} 229 return {'body': self._from_json(datastring)}
253 230
254 231
255class XMLDeserializer(TextDeserializer):
256
257 def __init__(self, metadata=None):
258 """
259 :param metadata: information needed to deserialize xml into
260 a dictionary.
261 """
262 super(XMLDeserializer, self).__init__()
263 self.metadata = metadata or {}
264
265 def _from_xml(self, datastring):
266 plurals = set(self.metadata.get('plurals', {}))
267
268 try:
269 node = utils.safe_minidom_parse_string(datastring).childNodes[0]
270 return {node.nodeName: self._from_xml_node(node, plurals)}
271 except expat.ExpatError:
272 msg = _("cannot understand XML")
273 raise exception.MalformedRequestBody(reason=msg)
274
275 def _from_xml_node(self, node, listnames):
276 """Convert a minidom node to a simple Python type.
277
278 :param listnames: list of XML node names whose subnodes should
279 be considered list items.
280
281 """
282 if len(node.childNodes) == 1 and node.childNodes[0].nodeType == 3:
283 return node.childNodes[0].nodeValue
284 elif node.nodeName in listnames:
285 return [self._from_xml_node(n, listnames) for n in node.childNodes]
286 else:
287 result = dict()
288 for attr in node.attributes.keys():
289 result[attr] = node.attributes[attr].nodeValue
290 for child in node.childNodes:
291 if child.nodeType != node.TEXT_NODE:
292 result[child.nodeName] = self._from_xml_node(child,
293 listnames)
294 return result
295
296 def find_first_child_named(self, parent, name):
297 """Search a nodes children for the first child with a given name"""
298 for node in parent.childNodes:
299 if node.nodeName == name:
300 return node
301 return None
302
303 def find_children_named(self, parent, name):
304 """Return all of a nodes children who have the given name"""
305 for node in parent.childNodes:
306 if node.nodeName == name:
307 yield node
308
309 def extract_text(self, node):
310 """Get the text field contained by the given node"""
311 if len(node.childNodes) == 1:
312 child = node.childNodes[0]
313 if child.nodeType == child.TEXT_NODE:
314 return child.nodeValue
315 return ""
316
317 def find_attribute_or_element(self, parent, name):
318 """Get an attribute value; fallback to an element if not found"""
319 if parent.hasAttribute(name):
320 return parent.getAttribute(name)
321
322 node = self.find_first_child_named(parent, name)
323 if node:
324 return self.extract_text(node)
325
326 return None
327
328 def default(self, datastring):
329 return {'body': self._from_xml(datastring)}
330
331
332class MetadataXMLDeserializer(XMLDeserializer):
333
334 def extract_metadata(self, metadata_node):
335 """Marshal the metadata attribute of a parsed request"""
336 metadata = {}
337 if metadata_node is not None:
338 for meta_node in self.find_children_named(metadata_node, "meta"):
339 key = meta_node.getAttribute("key")
340 metadata[key] = self.extract_text(meta_node)
341 return metadata
342
343
344class DictSerializer(ActionDispatcher): 232class DictSerializer(ActionDispatcher):
345 """Default request body serialization""" 233 """Default request body serialization"""
346 234
@@ -358,110 +246,6 @@ class JSONDictSerializer(DictSerializer):
358 return jsonutils.dumps(data) 246 return jsonutils.dumps(data)
359 247
360 248
361class XMLDictSerializer(DictSerializer):
362
363 def __init__(self, metadata=None, xmlns=None):
364 """
365 :param metadata: information needed to deserialize xml into
366 a dictionary.
367 :param xmlns: XML namespace to include with serialized xml
368 """
369 super(XMLDictSerializer, self).__init__()
370 self.metadata = metadata or {}
371 self.xmlns = xmlns
372
373 def default(self, data):
374 # We expect data to contain a single key which is the XML root.
375 root_key = data.keys()[0]
376 doc = minidom.Document()
377 node = self._to_xml_node(doc, self.metadata, root_key, data[root_key])
378
379 return self.to_xml_string(node)
380
381 def to_xml_string(self, node, has_atom=False):
382 self._add_xmlns(node, has_atom)
383 return node.toxml('UTF-8')
384
385 # NOTE (ameade): the has_atom should be removed after all of the
386 # xml serializers and view builders have been updated to the current
387 # spec that required all responses include the xmlns:atom, the has_atom
388 # flag is to prevent current tests from breaking
389 def _add_xmlns(self, node, has_atom=False):
390 if self.xmlns is not None:
391 node.setAttribute('xmlns', self.xmlns)
392 if has_atom:
393 node.setAttribute('xmlns:atom', "http://www.w3.org/2005/Atom")
394
395 def _to_xml_node(self, doc, metadata, nodename, data):
396 """Recursive method to convert data members to XML nodes."""
397 result = doc.createElement(nodename)
398
399 # Set the xml namespace if one is specified
400 # TODO(justinsb): We could also use prefixes on the keys
401 xmlns = metadata.get('xmlns', None)
402 if xmlns:
403 result.setAttribute('xmlns', xmlns)
404
405 # TODO(bcwaldon): accomplish this without a type-check
406 if isinstance(data, list):
407 collections = metadata.get('list_collections', {})
408 if nodename in collections:
409 metadata = collections[nodename]
410 for item in data:
411 node = doc.createElement(metadata['item_name'])
412 node.setAttribute(metadata['item_key'], str(item))
413 result.appendChild(node)
414 return result
415 singular = metadata.get('plurals', {}).get(nodename, None)
416 if singular is None:
417 if nodename.endswith('s'):
418 singular = nodename[:-1]
419 else:
420 singular = 'item'
421 for item in data:
422 node = self._to_xml_node(doc, metadata, singular, item)
423 result.appendChild(node)
424 # TODO(bcwaldon): accomplish this without a type-check
425 elif isinstance(data, dict):
426 collections = metadata.get('dict_collections', {})
427 if nodename in collections:
428 metadata = collections[nodename]
429 for k, v in data.items():
430 node = doc.createElement(metadata['item_name'])
431 node.setAttribute(metadata['item_key'], str(k))
432 text = doc.createTextNode(str(v))
433 node.appendChild(text)
434 result.appendChild(node)
435 return result
436 attrs = metadata.get('attributes', {}).get(nodename, {})
437 for k, v in data.items():
438 if k in attrs:
439 result.setAttribute(k, str(v))
440 else:
441 node = self._to_xml_node(doc, metadata, k, v)
442 result.appendChild(node)
443 else:
444 # Type is atom
445 node = doc.createTextNode(str(data))
446 result.appendChild(node)
447 return result
448
449 def _create_link_nodes(self, xml_doc, links):
450 link_nodes = []
451 for link in links:
452 link_node = xml_doc.createElement('atom:link')
453 link_node.setAttribute('rel', link['rel'])
454 link_node.setAttribute('href', link['href'])
455 if 'type' in link:
456 link_node.setAttribute('type', link['type'])
457 link_nodes.append(link_node)
458 return link_nodes
459
460 def _to_xml(self, root):
461 """Convert the xml object to an xml string."""
462 return etree.tostring(root, encoding='UTF-8', xml_declaration=True)
463
464
465def serializers(**serializers): 249def serializers(**serializers):
466 """Attaches serializers to a method. 250 """Attaches serializers to a method.
467 251
@@ -660,15 +444,6 @@ def action_peek_json(body):
660 return decoded.keys()[0] 444 return decoded.keys()[0]
661 445
662 446
663def action_peek_xml(body):
664 """Determine action to invoke."""
665
666 dom = utils.safe_minidom_parse_string(body)
667 action_node = dom.childNodes[0]
668
669 return action_node.tagName
670
671
672class ResourceExceptionHandler(object): 447class ResourceExceptionHandler(object):
673 """Context manager to handle Resource exceptions. 448 """Context manager to handle Resource exceptions.
674 449
@@ -731,16 +506,13 @@ class Resource(wsgi.Application):
731 506
732 self.controller = controller 507 self.controller = controller
733 508
734 default_deserializers = dict(xml=XMLDeserializer, 509 default_deserializers = dict(json=JSONDeserializer)
735 json=JSONDeserializer)
736 default_deserializers.update(deserializers) 510 default_deserializers.update(deserializers)
737 511
738 self.default_deserializers = default_deserializers 512 self.default_deserializers = default_deserializers
739 self.default_serializers = dict(xml=XMLDictSerializer, 513 self.default_serializers = dict(json=JSONDictSerializer)
740 json=JSONDictSerializer)
741 514
742 self.action_peek = dict(xml=action_peek_xml, 515 self.action_peek = dict(json=action_peek_json)
743 json=action_peek_json)
744 self.action_peek.update(action_peek or {}) 516 self.action_peek.update(action_peek or {})
745 517
746 # Copy over the actions dictionary 518 # Copy over the actions dictionary
@@ -1186,11 +958,8 @@ class Fault(webob.exc.HTTPException):
1186 # 'code' is an attribute on the fault tag itself 958 # 'code' is an attribute on the fault tag itself
1187 metadata = {'attributes': {fault_name: 'code'}} 959 metadata = {'attributes': {fault_name: 'code'}}
1188 960
1189 xml_serializer = XMLDictSerializer(metadata, XMLNS_V1)
1190
1191 content_type = req.best_match_content_type() 961 content_type = req.best_match_content_type()
1192 serializer = { 962 serializer = {
1193 'application/xml': xml_serializer,
1194 'application/json': JSONDictSerializer(), 963 'application/json': JSONDictSerializer(),
1195 }[content_type] 964 }[content_type]
1196 965
@@ -1245,9 +1014,7 @@ class OverLimitFault(webob.exc.HTTPException):
1245 content_type = request.best_match_content_type() 1014 content_type = request.best_match_content_type()
1246 metadata = {"attributes": {"overLimitFault": "code"}} 1015 metadata = {"attributes": {"overLimitFault": "code"}}
1247 1016
1248 xml_serializer = XMLDictSerializer(metadata, XMLNS_V1)
1249 serializer = { 1017 serializer = {
1250 'application/xml': xml_serializer,
1251 'application/json': JSONDictSerializer(), 1018 'application/json': JSONDictSerializer(),
1252 }[content_type] 1019 }[content_type]
1253 1020
diff --git a/manila/api/schemas/atom-link.rng b/manila/api/schemas/atom-link.rng
deleted file mode 100644
index edba5ee..0000000
--- a/manila/api/schemas/atom-link.rng
+++ /dev/null
@@ -1,141 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!--
3 -*- rnc -*-
4 RELAX NG Compact Syntax Grammar for the
5 Atom Format Specification Version 11
6-->
7<grammar xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:s="http://www.ascc.net/xml/schematron" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
8 <start>
9 <choice>
10 <ref name="atomLink"/>
11 </choice>
12 </start>
13 <!-- Common attributes -->
14 <define name="atomCommonAttributes">
15 <optional>
16 <attribute name="xml:base">
17 <ref name="atomUri"/>
18 </attribute>
19 </optional>
20 <optional>
21 <attribute name="xml:lang">
22 <ref name="atomLanguageTag"/>
23 </attribute>
24 </optional>
25 <zeroOrMore>
26 <ref name="undefinedAttribute"/>
27 </zeroOrMore>
28 </define>
29 <!-- atom:link -->
30 <define name="atomLink">
31 <element name="atom:link">
32 <ref name="atomCommonAttributes"/>
33 <attribute name="href">
34 <ref name="atomUri"/>
35 </attribute>
36 <optional>
37 <attribute name="rel">
38 <choice>
39 <ref name="atomNCName"/>
40 <ref name="atomUri"/>
41 </choice>
42 </attribute>
43 </optional>
44 <optional>
45 <attribute name="type">
46 <ref name="atomMediaType"/>
47 </attribute>
48 </optional>
49 <optional>
50 <attribute name="hreflang">
51 <ref name="atomLanguageTag"/>
52 </attribute>
53 </optional>
54 <optional>
55 <attribute name="title"/>
56 </optional>
57 <optional>
58 <attribute name="length"/>
59 </optional>
60 <ref name="undefinedContent"/>
61 </element>
62 </define>
63 <!-- Low-level simple types -->
64 <define name="atomNCName">
65 <data type="string">
66 <param name="minLength">1</param>
67 <param name="pattern">[^:]*</param>
68 </data>
69 </define>
70 <!-- Whatever a media type is, it contains at least one slash -->
71 <define name="atomMediaType">
72 <data type="string">
73 <param name="pattern">.+/.+</param>
74 </data>
75 </define>
76 <!-- As defined in RFC 3066 -->
77 <define name="atomLanguageTag">
78 <data type="string">
79 <param name="pattern">[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*</param>
80 </data>
81 </define>
82 <!--
83 Unconstrained; it's not entirely clear how IRI fit into
84 xsd:anyURI so let's not try to constrain it here
85 -->
86 <define name="atomUri">
87 <text/>
88 </define>
89 <!-- Other Extensibility -->
90 <define name="undefinedAttribute">
91 <attribute>
92 <anyName>
93 <except>
94 <name>xml:base</name>
95 <name>xml:lang</name>
96 <nsName ns=""/>
97 </except>
98 </anyName>
99 </attribute>
100 </define>
101 <define name="undefinedContent">
102 <zeroOrMore>
103 <choice>
104 <text/>
105 <ref name="anyForeignElement"/>
106 </choice>
107 </zeroOrMore>
108 </define>
109 <define name="anyElement">
110 <element>
111 <anyName/>
112 <zeroOrMore>
113 <choice>
114 <attribute>
115 <anyName/>
116 </attribute>
117 <text/>
118 <ref name="anyElement"/>
119 </choice>
120 </zeroOrMore>
121 </element>
122 </define>
123 <define name="anyForeignElement">
124 <element>
125 <anyName>
126 <except>
127 <nsName ns="http://www.w3.org/2005/Atom"/>
128 </except>
129 </anyName>
130 <zeroOrMore>
131 <choice>
132 <attribute>
133 <anyName/>
134 </attribute>
135 <text/>
136 <ref name="anyElement"/>
137 </choice>
138 </zeroOrMore>
139 </element>
140 </define>
141</grammar>
diff --git a/manila/api/schemas/v1.1/extension.rng b/manila/api/schemas/v1.1/extension.rng
deleted file mode 100644
index b16d8c1..0000000
--- a/manila/api/schemas/v1.1/extension.rng
+++ /dev/null
@@ -1,11 +0,0 @@
1<element name="extension" ns="http://docs.openstack.org/common/api/v1.0"
2 xmlns="http://relaxng.org/ns/structure/1.0">
3 <attribute name="alias"> <text/> </attribute>
4 <attribute name="name"> <text/> </attribute>
5 <attribute name="namespace"> <text/> </attribute>
6 <attribute name="updated"> <text/> </attribute>
7 <element name="description"> <text/> </element>
8 <zeroOrMore>
9 <externalRef href="../atom-link.rng"/>
10 </zeroOrMore>
11</element>
diff --git a/manila/api/schemas/v1.1/extensions.rng b/manila/api/schemas/v1.1/extensions.rng
deleted file mode 100644
index 8538eaf..0000000
--- a/manila/api/schemas/v1.1/extensions.rng
+++ /dev/null
@@ -1,6 +0,0 @@
1<element name="extensions" xmlns="http://relaxng.org/ns/structure/1.0"
2 ns="http://docs.openstack.org/common/api/v1.0">
3 <zeroOrMore>
4 <externalRef href="extension.rng"/>
5 </zeroOrMore>
6</element>
diff --git a/manila/api/schemas/v1.1/limits.rng b/manila/api/schemas/v1.1/limits.rng
deleted file mode 100644
index a66af4b..0000000
--- a/manila/api/schemas/v1.1/limits.rng
+++ /dev/null
@@ -1,28 +0,0 @@
1<element name="limits" ns="http://docs.openstack.org/common/api/v1.0"
2 xmlns="http://relaxng.org/ns/structure/1.0">
3 <element name="rates">
4 <zeroOrMore>
5 <element name="rate">
6 <attribute name="uri"> <text/> </attribute>
7 <attribute name="regex"> <text/> </attribute>
8 <zeroOrMore>
9 <element name="limit">
10 <attribute name="value"> <text/> </attribute>
11 <attribute name="verb"> <text/> </attribute>
12 <attribute name="remaining"> <text/> </attribute>
13 <attribute name="unit"> <text/> </attribute>
14 <attribute name="next-available"> <text/> </attribute>
15 </element>
16 </zeroOrMore>
17 </element>
18 </zeroOrMore>
19 </element>
20 <element name="absolute">
21 <zeroOrMore>
22 <element name="limit">
23 <attribute name="name"> <text/> </attribute>
24 <attribute name="value"> <text/> </attribute>
25 </element>
26 </zeroOrMore>
27 </element>
28</element>
diff --git a/manila/api/schemas/v1.1/metadata.rng b/manila/api/schemas/v1.1/metadata.rng
deleted file mode 100644
index b2f5d70..0000000
--- a/manila/api/schemas/v1.1/metadata.rng
+++ /dev/null
@@ -1,9 +0,0 @@
1 <element name="metadata" ns="http://docs.openstack.org/compute/api/v1.1"
2 xmlns="http://relaxng.org/ns/structure/1.0">
3 <zeroOrMore>
4 <element name="meta">
5 <attribute name="key"> <text/> </attribute>
6 <text/>
7 </element>
8 </zeroOrMore>
9 </element>
diff --git a/manila/api/urlmap.py b/manila/api/urlmap.py
index 3a22324..09493db 100644
--- a/manila/api/urlmap.py
+++ b/manila/api/urlmap.py
@@ -253,24 +253,16 @@ class URLMap(paste.urlmap.URLMap):
253 path_info = environ['PATH_INFO'] 253 path_info = environ['PATH_INFO']
254 path_info = self.normalize_url(path_info, False)[1] 254 path_info = self.normalize_url(path_info, False)[1]
255 255
256 # The MIME type for the response is determined in one of two ways:
257 # 1) URL path suffix (eg /servers/detail.json)
258 # 2) Accept header (eg application/json;q=0.8, application/xml;q=0.2)
259
260 # The API version is determined in one of three ways: 256 # The API version is determined in one of three ways:
261 # 1) URL path prefix (eg /v1.1/tenant/servers/detail) 257 # 1) URL path prefix (eg /v1.1/tenant/servers/detail)
262 # 2) Content-Type header (eg application/json;version=1.1) 258 # 2) Content-Type header (eg application/json;version=1.1)
263 # 3) Accept header (eg application/json;q=0.8;version=1.1) 259 # 3) Accept header (eg application/json;q=0.8;version=1.1)
264 260
261 # Manila supports only application/json as MIME type for the responses.
265 supported_content_types = list(wsgi.SUPPORTED_CONTENT_TYPES) 262 supported_content_types = list(wsgi.SUPPORTED_CONTENT_TYPES)
266 263
267 mime_type, app, app_url = self._path_strategy(host, port, path_info) 264 mime_type, app, app_url = self._path_strategy(host, port, path_info)
268 265
269 # Accept application/atom+xml for the index query of each API
270 # version mount point as well as the root index
271 if (app_url and app_url + '/' == path_info) or path_info == '/':
272 supported_content_types.append('application/atom+xml')
273
274 if not app: 266 if not app:
275 app = self._content_type_strategy(host, port, environ) 267 app = self._content_type_strategy(host, port, environ)
276 268
diff --git a/manila/api/v1/limits.py b/manila/api/v1/limits.py
index 6c3c59f..fae0dfc 100644
--- a/manila/api/v1/limits.py
+++ b/manila/api/v1/limits.py
@@ -31,7 +31,6 @@ import webob.exc
31 31
32from manila.api.openstack import wsgi 32from manila.api.openstack import wsgi
33from manila.api.views import limits as limits_views 33from manila.api.views import limits as limits_views
34from manila.api import xmlutil
35from manila.i18n import _ 34from manila.i18n import _
36from manila import quota 35from manila import quota
37from manila import wsgi as base_wsgi 36from manila import wsgi as base_wsgi
@@ -46,38 +45,9 @@ PER_HOUR = 60 * 60
46PER_DAY = 60 * 60 * 24 45PER_DAY = 60 * 60 * 24
47 46
48 47
49limits_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
50
51
52class LimitsTemplate(xmlutil.TemplateBuilder):
53 def construct(self):
54 root = xmlutil.TemplateElement('limits', selector='limits')
55
56 rates = xmlutil.SubTemplateElement(root, 'rates')
57 rate = xmlutil.SubTemplateElement(rates, 'rate', selector='rate')
58 rate.set('uri', 'uri')
59 rate.set('regex', 'regex')
60 limit = xmlutil.SubTemplateElement(rate, 'limit', selector='limit')
61 limit.set('value', 'value')
62 limit.set('verb', 'verb')
63 limit.set('remaining', 'remaining')
64 limit.set('unit', 'unit')
65 limit.set('next-available', 'next-available')
66
67 absolute = xmlutil.SubTemplateElement(root, 'absolute',
68 selector='absolute')
69 limit = xmlutil.SubTemplateElement(absolute, 'limit',
70 selector=xmlutil.get_items)
71 limit.set('name', 0)
72 limit.set('value', 1)
73
74 return xmlutil.MasterTemplate(root, 1, nsmap=limits_nsmap)
75
76
77class LimitsController(object): 48class LimitsController(object):
78 """Controller for accessing limits in the OpenStack API.""" 49 """Controller for accessing limits in the OpenStack API."""
79 50
80 @wsgi.serializers(xml=LimitsTemplate)
81 def index(self, req): 51 def index(self, req):
82 """Return all global and rate limit information.""" 52 """Return all global and rate limit information."""
83 context = req.environ['manila.context'] 53 context = req.environ['manila.context']
diff --git a/manila/api/v1/security_service.py b/manila/api/v1/security_service.py
index 4c3ad59..6871b6f 100644
--- a/manila/api/v1/security_service.py
+++ b/manila/api/v1/security_service.py
@@ -23,7 +23,6 @@ from webob import exc
23from manila.api import common 23from manila.api import common
24from manila.api.openstack import wsgi 24from manila.api.openstack import wsgi
25from manila.api.views import security_service as security_service_views 25from manila.api.views import security_service as security_service_views
26from manila.api import xmlutil
27from manila.common import constants 26from manila.common import constants
28from manila import db 27from manila import db
29from manila import exception 28from manila import exception
@@ -36,36 +35,11 @@ RESOURCE_NAME = 'security_service'
36LOG = log.getLogger(__name__) 35LOG = log.getLogger(__name__)
37 36
38 37
39def make_security_service(elem):
40 attrs = ['id', 'name', 'description', 'type', 'server', 'domain', 'user',
41 'password', 'dns_ip', 'status', 'updated_at', 'created_at']
42 for attr in attrs:
43 elem.set(attr)
44
45
46class SecurityServiceTemplate(xmlutil.TemplateBuilder):
47 def construct(self):
48 root = xmlutil.TemplateElement('security_service',
49 selector='security_service')
50 make_security_service(root)
51 return xmlutil.MasterTemplate(root, 1)
52
53
54class SecurityServicesTemplate(xmlutil.TemplateBuilder):
55 def construct(self):
56 root = xmlutil.TemplateElement('security_services')
57 elem = xmlutil.SubTemplateElement(root, 'security_service',
58 selector='security_services')
59 make_security_service(elem)
60 return xmlutil.MasterTemplate(root, 1)
61
62
63class SecurityServiceController(wsgi.Controller): 38class SecurityServiceController(wsgi.Controller):
64 """The Shares API controller for the OpenStack API.""" 39 """The Shares API controller for the OpenStack API."""
65 40
66 _view_builder_class = security_service_views.ViewBuilder 41 _view_builder_class = security_service_views.ViewBuilder
67 42
68 @wsgi.serializers(xml=SecurityServiceTemplate)
69 def show(self, req, id): 43 def show(self, req, id):
70 """Return data about the given security service.""" 44 """Return data about the given security service."""
71 context = req.environ['manila.context'] 45 context = req.environ['manila.context']
@@ -102,14 +76,12 @@ class SecurityServiceController(wsgi.Controller):
102 76
103 return webob.Response(status_int=202) 77 return webob.Response(status_int=202)
104 78
105 @wsgi.serializers(xml=SecurityServicesTemplate)
106 def index(self, req): 79 def index(self, req):
107 """Returns a summary list of security services.""" 80 """Returns a summary list of security services."""
108 policy.check_policy(req.environ['manila.context'], RESOURCE_NAME, 81 policy.check_policy(req.environ['manila.context'], RESOURCE_NAME,
109 'index') 82 'index')
110 return self._get_security_services(req, is_detail=False) 83 return self._get_security_services(req, is_detail=False)
111 84
112 @wsgi.serializers(xml=SecurityServicesTemplate)
113 def detail(self, req): 85 def detail(self, req):
114 """Returns a detailed list of security services.""" 86 """Returns a detailed list of security services."""
115 policy.check_policy(req.environ['manila.context'], RESOURCE_NAME, 87 policy.check_policy(req.environ['manila.context'], RESOURCE_NAME,
@@ -181,7 +153,6 @@ class SecurityServiceController(wsgi.Controller):
181 return True 153 return True
182 return False 154 return False
183 155
184 @wsgi.serializers(xml=SecurityServiceTemplate)
185 def update(self, req, id, body): 156 def update(self, req, id, body):
186 """Update a security service.""" 157 """Update a security service."""
187 context = req.environ['manila.context'] 158 context = req.environ['manila.context']
@@ -221,7 +192,6 @@ class SecurityServiceController(wsgi.Controller):
221 security_service = db.security_service_update(context, id, update_dict) 192 security_service = db.security_service_update(context, id, update_dict)
222 return self._view_builder.detail(req, security_service) 193 return self._view_builder.detail(req, security_service)
223 194
224 @wsgi.serializers(xml=SecurityServiceTemplate)
225 def create(self, req, body): 195 def create(self, req, body):
226 """Creates a new security service.""" 196 """Creates a new security service."""
227 context = req.environ['manila.context'] 197 context = req.environ['manila.context']
diff --git a/manila/api/v1/share_metadata.py b/manila/api/v1/share_metadata.py
index 7d212dd..971c1b2 100644
--- a/manila/api/v1/share_metadata.py
+++ b/manila/api/v1/share_metadata.py
@@ -16,7 +16,6 @@
16import webob 16import webob
17from webob import exc 17from webob import exc
18 18
19from manila.api import common
20from manila.api.openstack import wsgi 19from manila.api.openstack import wsgi
21from manila import exception 20from manila import exception
22from manila.i18n import _ 21from manila.i18n import _
@@ -39,14 +38,11 @@ class ShareMetadataController(object):
39 raise exc.HTTPNotFound(explanation=msg) 38 raise exc.HTTPNotFound(explanation=msg)
40 return meta 39 return meta
41 40
42 @wsgi.serializers(xml=common.MetadataTemplate)
43 def index(self, req, share_id): 41 def index(self, req, share_id):
44 """Returns the list of metadata for a given share.""" 42 """Returns the list of metadata for a given share."""
45 context = req.environ['manila.context'] 43 context = req.environ['manila.context']
46 return {'metadata': self._get_metadata(context, share_id)} 44 return {'metadata': self._get_metadata(context, share_id)}
47 45
48 @wsgi.serializers(xml=common.MetadataTemplate)
49 @wsgi.deserializers(xml=common.MetadataDeserializer)
50 def create(self, req, share_id, body): 46 def create(self, req, share_id, body):
51 try: 47 try:
52 metadata = body['metadata'] 48 metadata = body['metadata']
@@ -63,8 +59,6 @@ class ShareMetadataController(object):
63 59
64 return {'metadata': new_metadata} 60 return {'metadata': new_metadata}
65 61
66 @wsgi.serializers(xml=common.MetaItemTemplate)
67 @wsgi.deserializers(xml=common.MetaItemDeserializer)
68 def update(self, req, share_id, id, body): 62 def update(self, req, share_id, id, body):
69 try: 63 try:
70 meta_item = body['meta'] 64 meta_item = body['meta']
@@ -88,8 +82,6 @@ class ShareMetadataController(object):
88 82
89 return {'meta': meta_item} 83 return {'meta': meta_item}
90 84
91 @wsgi.serializers(xml=common.MetadataTemplate)
92 @wsgi.deserializers(xml=common.MetadataDeserializer)
93 def update_all(self, req, share_id, body): 85 def update_all(self, req, share_id, body):
94 try: 86 try:
95 metadata = body['metadata'] 87 metadata = body['metadata']
@@ -125,7 +117,6 @@ class ShareMetadataController(object):
125 except exception.InvalidShareMetadataSize as error: 117 except exception.InvalidShareMetadataSize as error:
126 raise exc.HTTPBadRequest(explanation=error.msg) 118 raise exc.HTTPBadRequest(explanation=error.msg)
127 119
128 @wsgi.serializers(xml=common.MetaItemTemplate)
129 def show(self, req, share_id, id): 120 def show(self, req, share_id, id):
130 """Return a single metadata item.""" 121 """Return a single metadata item."""
131 context = req.environ['manila.context'] 122 context = req.environ['manila.context']
diff --git a/manila/api/v1/share_networks.py b/manila/api/v1/share_networks.py
index e2a9d7f..6459986 100644
--- a/manila/api/v1/share_networks.py
+++ b/manila/api/v1/share_networks.py
@@ -25,7 +25,6 @@ from webob import exc
25from manila.api import common 25from manila.api import common
26from manila.api.openstack import wsgi 26from manila.api.openstack import wsgi
27from manila.api.views import share_networks as share_networks_views 27from manila.api.views import share_networks as share_networks_views
28from manila.api import xmlutil
29from manila.db import api as db_api 28from manila.db import api as db_api
30from manila import exception 29from manila import exception
31from manila.i18n import _ 30from manila.i18n import _
@@ -39,43 +38,6 @@ RESOURCE_NAME = 'share_network'
39RESOURCES_NAME = 'share_networks' 38RESOURCES_NAME = 'share_networks'
40LOG = log.getLogger(__name__) 39LOG = log.getLogger(__name__)
41QUOTAS = quota.QUOTAS 40QUOTAS = quota.QUOTAS
42SHARE_NETWORK_ATTRS = (
43 'id',
44 'project_id',
45 'user_id',
46 'created_at',
47 'updated_at',
48 'nova_net_id',
49 'neutron_net_id',
50 'neutron_subnet_id',
51 'network_type',
52 'segmentation_id',
53 'cidr',
54 'ip_version',
55 'name',
56 'description',
57)
58
59
60def _make_share_network(elem):
61 for attr in SHARE_NETWORK_ATTRS:
62 elem.set(attr)
63
64
65class ShareNetworkTemplate(xmlutil.TemplateBuilder):
66 def construct(self):
67 root = xmlutil.TemplateElement(RESOURCE_NAME, selector=RESOURCE_NAME)
68 _make_share_network(root)
69 return xmlutil.MasterTemplate(root, 1)
70
71
72class ShareNetworksTemplate(xmlutil.TemplateBuilder):
73 def construct(self):
74 root = xmlutil.TemplateElement(RESOURCES_NAME)
75 elem = xmlutil.SubTemplateElement(root, RESOURCE_NAME,
76 selector=RESOURCES_NAME)
77 _make_share_network(elem)
78 return xmlutil.MasterTemplate(root, 1)
79 41
80 42
81class ShareNetworkController(wsgi.Controller): 43class ShareNetworkController(wsgi.Controller):
@@ -87,7 +49,6 @@ class ShareNetworkController(wsgi.Controller):
87 super(ShareNetworkController, self).__init__() 49 super(ShareNetworkController, self).__init__()
88 self.share_rpcapi = share_rpcapi.ShareAPI() 50 self.share_rpcapi = share_rpcapi.ShareAPI()
89 51
90 @wsgi.serializers(xml=ShareNetworkTemplate)
91 def show(self, req, id): 52 def show(self, req, id):
92 """Return data about the requested network info.""" 53 """Return data about the requested network info."""
93 context = req.environ['manila.context'] 54 context = req.environ['manila.context']
@@ -132,7 +93,6 @@ class ShareNetworkController(wsgi.Controller):
132 project_id=share_network['project_id']) 93 project_id=share_network['project_id'])
133 return webob.Response(status_int=202) 94 return webob.Response(status_int=202)
134 95
135 @wsgi.serializers(xml=ShareNetworksTemplate)
136 def _get_share_networks(self, req, is_detail=True): 96 def _get_share_networks(self, req, is_detail=True):
137 """Returns a list of share networks.""" 97 """Returns a list of share networks."""
138 context = req.environ['manila.context'] 98 context = req.environ['manila.context']
@@ -200,14 +160,12 @@ class ShareNetworkController(wsgi.Controller):
200 limited_list = common.limited(networks, req) 160 limited_list = common.limited(networks, req)
201 return self._view_builder.build_share_networks(limited_list, is_detail) 161 return self._view_builder.build_share_networks(limited_list, is_detail)
202 162
203 @wsgi.serializers(xml=ShareNetworksTemplate)
204 def index(self, req): 163 def index(self, req):
205 """Returns a summary list of share networks.""" 164 """Returns a summary list of share networks."""
206 policy.check_policy(req.environ['manila.context'], RESOURCE_NAME, 165 policy.check_policy(req.environ['manila.context'], RESOURCE_NAME,
207 'index') 166 'index')
208 return self._get_share_networks(req, is_detail=False) 167 return self._get_share_networks(req, is_detail=False)
209 168
210 @wsgi.serializers(xml=ShareNetworksTemplate)
211 def detail(self, req): 169 def detail(self, req):
212 """Returns a detailed list of share networks.""" 170 """Returns a detailed list of share networks."""
213 policy.check_policy(req.environ['manila.context'], RESOURCE_NAME, 171 policy.check_policy(req.environ['manila.context'], RESOURCE_NAME,
@@ -231,7 +189,6 @@ class ShareNetworkController(wsgi.Controller):
231 "exclusive. Only one of these are allowed at a time.") 189 "exclusive. Only one of these are allowed at a time.")
232 raise exc.HTTPBadRequest(explanation=msg) 190 raise exc.HTTPBadRequest(explanation=msg)
233 191
234 @wsgi.serializers(xml=ShareNetworkTemplate)
235 def update(self, req, id, body): 192 def update(self, req, id, body):
236 """Update specified share network.""" 193 """Update specified share network."""
237 context = req.environ['manila.context'] 194 context = req.environ['manila.context']
@@ -267,7 +224,6 @@ class ShareNetworkController(wsgi.Controller):
267 224
268 return self._view_builder.build_share_network(share_network) 225 return self._view_builder.build_share_network(share_network)
269 226
270 @wsgi.serializers(xml=ShareNetworkTemplate)
271 def create(self, req, body): 227 def create(self, req, body):
272 """Creates a new share network.""" 228 """Creates a new share network."""
273 context = req.environ['manila.context'] 229 context = req.environ['manila.context']
@@ -309,7 +265,6 @@ class ShareNetworkController(wsgi.Controller):
309 QUOTAS.commit(context, reservations) 265 QUOTAS.commit(context, reservations)
310 return self._view_builder.build_share_network(share_network) 266 return self._view_builder.build_share_network(share_network)
311 267
312 @wsgi.serializers(xml=ShareNetworkTemplate)
313 def action(self, req, id, body): 268 def action(self, req, id, body):
314 _actions = { 269 _actions = {
315 'add_security_service': self._add_security_service, 270 'add_security_service': self._add_security_service,
diff --git a/manila/api/v1/share_servers.py b/manila/api/v1/share_servers.py
index 2c98faf..352c156 100644
--- a/manila/api/v1/share_servers.py
+++ b/manila/api/v1/share_servers.py
@@ -20,7 +20,6 @@ from webob import exc
20 20
21from manila.api.openstack import wsgi 21from manila.api.openstack import wsgi
22from manila.api.views import share_servers as share_servers_views 22from manila.api.views import share_servers as share_servers_views
23from manila.api import xmlutil
24from manila.common import constants 23from manila.common import constants
25from manila.db import api as db_api 24from manila.db import api as db_api
26from manila import exception 25from manila import exception
@@ -31,37 +30,6 @@ from manila import share
31RESOURCE_NAME = 'share_server' 30RESOURCE_NAME = 'share_server'
32RESOURCES_NAME = 'share_servers' 31RESOURCES_NAME = 'share_servers'
33LOG = log.getLogger(__name__) 32LOG = log.getLogger(__name__)
34SHARE_SERVER_ATTRS = (
35 'id',
36 'project_id',
37 'updated_at',
38 'status',
39 'host',
40 'share_network_name',
41)
42
43
44def _make_share_server(elem, details=False):
45 for attr in SHARE_SERVER_ATTRS:
46 elem.set(attr)
47 if details:
48 elem.set('backend_details')
49
50
51class ShareServerTemplate(xmlutil.TemplateBuilder):
52 def construct(self):
53 root = xmlutil.TemplateElement(RESOURCE_NAME, selector=RESOURCE_NAME)
54 _make_share_server(root, details=True)
55 return xmlutil.MasterTemplate(root, 1)
56
57
58class ShareServersTemplate(xmlutil.TemplateBuilder):
59 def construct(self):
60 root = xmlutil.TemplateElement(RESOURCES_NAME)
61 elem = xmlutil.SubTemplateElement(root, RESOURCE_NAME,
62 selector=RESOURCES_NAME)
63 _make_share_server(elem)
64 return xmlutil.MasterTemplate(root, 1)
65 33
66 34
67class ShareServerController(wsgi.Controller): 35class ShareServerController(wsgi.Controller):
@@ -72,7 +40,6 @@ class ShareServerController(wsgi.Controller):
72 self._view_builder_class = share_servers_views.ViewBuilder 40 self._view_builder_class = share_servers_views.ViewBuilder
73 super(ShareServerController, self).__init__() 41 super(ShareServerController, self).__init__()
74 42
75 @wsgi.serializers(xml=ShareServersTemplate)
76 def index(self, req): 43 def index(self, req):
77 """Returns a list of share servers.""" 44 """Returns a list of share servers."""
78 45
@@ -98,7 +65,6 @@ class ShareServerController(wsgi.Controller):
98 s.share_network['id']])] 65 s.share_network['id']])]
99 return self._view_builder.build_share_servers(share_servers) 66 return self._view_builder.build_share_servers(share_servers)
100 67
101 @wsgi.serializers(xml=ShareServerTemplate)
102 def show(self, req, id): 68 def show(self, req, id):
103 """Return data about the requested share server.""" 69 """Return data about the requested share server."""
104 context = req.environ['manila.context'] 70 context = req.environ['manila.context']
diff --git a/manila/api/v1/share_snapshots.py b/manila/api/v1/share_snapshots.py
index 0feb6bc..10db814 100644
--- a/manila/api/v1/share_snapshots.py
+++ b/manila/api/v1/share_snapshots.py
@@ -23,7 +23,6 @@ from webob import exc
23from manila.api import common 23from manila.api import common
24from manila.api.openstack import wsgi 24from manila.api.openstack import wsgi
25from manila.api.views import share_snapshots as snapshot_views 25from manila.api.views import share_snapshots as snapshot_views
26from manila.api import xmlutil
27from manila import exception 26from manila import exception
28from manila.i18n import _LI 27from manila.i18n import _LI
29from manila import share 28from manila import share
@@ -31,30 +30,6 @@ from manila import share
31LOG = log.getLogger(__name__) 30LOG = log.getLogger(__name__)
32 31
33 32
34def make_snapshot(elem):
35 attrs = ['id', 'size', 'status', 'name', 'description', 'share_proto',
36 'links', 'share_id', 'created_at', 'share_size']
37 for attr in attrs:
38 elem.set(attr)
39
40
41class SnapshotTemplate(xmlutil.TemplateBuilder):
42 def construct(self):
43 root = xmlutil.TemplateElement('snapshot',
44 selector='snapshot')
45 make_snapshot(root)
46 return xmlutil.MasterTemplate(root, 1)
47
48
49class SnapshotsTemplate(xmlutil.TemplateBuilder):
50 def construct(self):
51 root = xmlutil.TemplateElement('snapshots')
52 elem = xmlutil.SubTemplateElement(root, 'snapshot',
53 selector='snapshots')
54 make_snapshot(elem)
55 return xmlutil.MasterTemplate(root, 1)
56
57
58class ShareSnapshotsController(wsgi.Controller): 33class ShareSnapshotsController(wsgi.Controller):
59 """The Share Snapshots API controller for the OpenStack API.""" 34 """The Share Snapshots API controller for the OpenStack API."""
60 35
@@ -64,7 +39,6 @@ class ShareSnapshotsController(wsgi.Controller):
64 super(ShareSnapshotsController, self).__init__() 39 super(ShareSnapshotsController, self).__init__()
65 self.share_api = share.API() 40 self.share_api = share.API()
66 41
67 @wsgi.serializers(xml=SnapshotTemplate)
68 def show(self, req, id): 42 def show(self, req, id):
69 """Return data about the given snapshot.""" 43 """Return data about the given snapshot."""
70 context = req.environ['manila.context'] 44 context = req.environ['manila.context']
@@ -89,12 +63,10 @@ class ShareSnapshotsController(wsgi.Controller):
89 raise exc.HTTPNotFound() 63 raise exc.HTTPNotFound()
90 return webob.Response(status_int=202) 64 return webob.Response(status_int=202)
91 65
92 @wsgi.serializers(xml=SnapshotsTemplate)
93 def index(self, req): 66 def index(self, req):
94 """Returns a summary list of snapshots.""" 67 """Returns a summary list of snapshots."""
95 return self._get_snapshots(req, is_detail=False) 68 return self._get_snapshots(req, is_detail=False)
96 69
97 @wsgi.serializers(xml=SnapshotsTemplate)
98 def detail(self, req): 70 def detail(self, req):
99 """Returns a detailed list of snapshots.""" 71 """Returns a detailed list of snapshots."""
100 return self._get_snapshots(req, is_detail=True) 72 return self._get_snapshots(req, is_detail=True)
@@ -138,7 +110,6 @@ class ShareSnapshotsController(wsgi.Controller):
138 """Return share search options allowed by non-admin.""" 110 """Return share search options allowed by non-admin."""
139 return ('display_name', 'name', 'status', 'share_id', 'size') 111 return ('display_name', 'name', 'status', 'share_id', 'size')
140 112
141 @wsgi.serializers(xml=SnapshotTemplate)
142 def update(self, req, id, body): 113 def update(self, req, id, body):
143 """Update a snapshot.""" 114 """Update a snapshot."""
144 context = req.environ['manila.context'] 115 context = req.environ['manila.context']
@@ -167,7 +138,6 @@ class ShareSnapshotsController(wsgi.Controller):
167 return self._view_builder.detail(req, snapshot) 138 return self._view_builder.detail(req, snapshot)
168 139
169 @wsgi.response(202) 140 @wsgi.response(202)
170 @wsgi.serializers(xml=SnapshotTemplate)
171 def create(self, req, body): 141 def create(self, req, body):
172 """Creates a new snapshot.""" 142 """Creates a new snapshot."""
173 context = req.environ['manila.context'] 143 context = req.environ['manila.context']
@@ -204,18 +174,3 @@ class ShareSnapshotsController(wsgi.Controller):
204 174
205def create_resource(): 175def create_resource():
206 return wsgi.Resource(ShareSnapshotsController()) 176 return wsgi.Resource(ShareSnapshotsController())
207
208#
209# class Share_snapshots(extensions.ExtensionDescriptor):
210# """Enable share snapshtos API."""
211# name = 'ShareSnapshots'
212# alias = 'snapshots'
213# namespace = ''
214# updated = '2013-03-01T00:00:00+00:00'
215#
216# def get_resources(self):
217# controller = ShareSnapshotsController()
218# resource = extensions.ResourceExtension(
219# 'snapshots', controller,
220# collection_actions={'detail': 'GET'})
221# return [resource]
diff --git a/manila/api/v1/shares.py b/manila/api/v1/shares.py
index 028222e..7e80765 100644
--- a/manila/api/v1/shares.py
+++ b/manila/api/v1/shares.py
@@ -26,7 +26,6 @@ from webob import exc
26from manila.api import common 26from manila.api import common
27from manila.api.openstack import wsgi 27from manila.api.openstack import wsgi
28from manila.api.views import shares as share_views 28from manila.api.views import shares as share_views
29from manila.api import xmlutil
30from manila import exception 29from manila import exception
31from manila.i18n import _ 30from manila.i18n import _
32from manila.i18n import _LI 31from manila.i18n import _LI
@@ -36,32 +35,6 @@ from manila.share import share_types
36LOG = log.getLogger(__name__) 35LOG = log.getLogger(__name__)
37 36
38 37
39def make_share(elem):
40 # NOTE(u_glide):
41 # export_location is backward-compatibility attribute, which contains first
42 # export location from export_locations list.
43 attrs = ['id', 'size', 'availability_zone', 'status', 'name',
44 'description', 'share_proto', 'export_location', 'links',
45 'snapshot_id', 'created_at', 'metadata', 'export_locations']
46 for attr in attrs:
47 elem.set(attr)
48
49
50class ShareTemplate(xmlutil.TemplateBuilder):
51 def construct(self):
52 root = xmlutil.TemplateElement('share', selector='share')
53 make_share(root)
54 return xmlutil.MasterTemplate(root, 1)
55
56
57class SharesTemplate(xmlutil.TemplateBuilder):
58 def construct(self):
59 root = xmlutil.TemplateElement('shares')
60 elem = xmlutil.SubTemplateElement(root, 'share', selector='shares')
61 make_share(elem)
62 return xmlutil.MasterTemplate(root, 1)
63
64
65class ShareController(wsgi.Controller): 38class ShareController(wsgi.Controller):
66 """The Shares API controller for the OpenStack API.""" 39 """The Shares API controller for the OpenStack API."""
67 40
@@ -71,7 +44,6 @@ class ShareController(wsgi.Controller):
71 super(ShareController, self).__init__() 44 super(ShareController, self).__init__()
72 self.share_api = share.API() 45 self.share_api = share.API()
73 46
74 @wsgi.serializers(xml=ShareTemplate)
75 def show(self, req, id): 47 def show(self, req, id):
76 """Return data about the given share.""" 48 """Return data about the given share."""
77 context = req.environ['manila.context'] 49 context = req.environ['manila.context']
@@ -99,12 +71,10 @@ class ShareController(wsgi.Controller):
99 71
100 return webob.Response(status_int=202) 72 return webob.Response(status_int=202)
101 73
102 @wsgi.serializers(xml=SharesTemplate)
103 def index(self, req): 74 def index(self, req):
104 """Returns a summary list of shares.""" 75 """Returns a summary list of shares."""
105 return self._get_shares(req, is_detail=False) 76 return self._get_shares(req, is_detail=False)
106 77
107 @wsgi.serializers(xml=SharesTemplate)
108 def detail(self, req): 78 def detail(self, req):
109 """Returns a detailed list of shares.""" 79 """Returns a detailed list of shares."""
110 return self._get_shares(req, is_detail=True) 80 return self._get_shares(req, is_detail=True)
@@ -164,7 +134,6 @@ class ShareController(wsgi.Controller):
164 'is_public', 'metadata', 'extra_specs', 'sort_key', 'sort_dir', 134 'is_public', 'metadata', 'extra_specs', 'sort_key', 'sort_dir',
165 ) 135 )
166 136
167 @wsgi.serializers(xml=ShareTemplate)
168 def update(self, req, id, body): 137 def update(self, req, id, body):
169 """Update a share.""" 138 """Update a share."""
170 context = req.environ['manila.context'] 139 context = req.environ['manila.context']
@@ -192,7 +161,6 @@ class ShareController(wsgi.Controller):
192 share.update(update_dict) 161 share.update(update_dict)
193 return self._view_builder.detail(req, share) 162 return self._view_builder.detail(req, share)
194 163
195 @wsgi.serializers(xml=ShareTemplate)
196 def create(self, req, body): 164 def create(self, req, body):
197 """Creates a new share.""" 165 """Creates a new share."""
198 context = req.environ['manila.context'] 166 context = req.environ['manila.context']
diff --git a/manila/api/versions.py b/manila/api/versions.py
index 5d87440..545e280 100644
--- a/manila/api/versions.py
+++ b/manila/api/versions.py
@@ -13,14 +13,10 @@
13# License for the specific language governing permissions and limitations 13# License for the specific language governing permissions and limitations
14# under the License. 14# under the License.
15 15
16import datetime
17
18from lxml import etree
19from oslo_config import cfg 16from oslo_config import cfg
20 17
21from manila.api.openstack import wsgi 18from manila.api.openstack import wsgi
22from manila.api.views import versions as views_versions 19from manila.api.views import versions as views_versions
23from manila.api import xmlutil
24 20
25CONF = cfg.CONF 21CONF = cfg.CONF
26 22
@@ -47,12 +43,7 @@ _KNOWN_VERSIONS = {
47 ], 43 ],
48 "media-types": [ 44 "media-types": [
49 { 45 {
50 "base": "application/xml",
51 "type": "application/vnd.openstack.volume+xml;version=1",
52 },
53 {
54 "base": "application/json", 46 "base": "application/json",
55 "type": "application/vnd.openstack.volume+json;version=1",
56 } 47 }
57 ], 48 ],
58 }, 49 },
@@ -77,12 +68,7 @@ _KNOWN_VERSIONS = {
77 ], 68 ],
78 "media-types": [ 69 "media-types": [
79 { 70 {
80 "base": "application/xml",
81 "type": "application/vnd.openstack.volume+xml;version=1",
82 },
83 {
84 "base": "application/json", 71 "base": "application/json",
85 "type": "application/vnd.openstack.volume+json;version=1",
86 } 72 }
87 ], 73 ],
88 } 74 }
@@ -101,157 +87,16 @@ def get_supported_versions():
101 return versions 87 return versions
102 88
103 89
104class MediaTypesTemplateElement(xmlutil.TemplateElement):
105 def will_render(self, datum):
106 return 'media-types' in datum
107
108
109def make_version(elem):
110 elem.set('id')
111 elem.set('status')
112 elem.set('updated')
113
114 mts = MediaTypesTemplateElement('media-types')
115 elem.append(mts)
116
117 mt = xmlutil.SubTemplateElement(mts, 'media-type', selector='media-types')
118 mt.set('base')
119 mt.set('type')
120
121 xmlutil.make_links(elem, 'links')
122
123
124version_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
125
126
127class VersionTemplate(xmlutil.TemplateBuilder):
128 def construct(self):
129 root = xmlutil.TemplateElement('version', selector='version')
130 make_version(root)
131 return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
132
133
134class VersionsTemplate(xmlutil.TemplateBuilder):
135 def construct(self):
136 root = xmlutil.TemplateElement('versions')
137 elem = xmlutil.SubTemplateElement(root, 'version', selector='versions')
138 make_version(elem)
139 return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
140
141
142class ChoicesTemplate(xmlutil.TemplateBuilder):
143 def construct(self):
144 root = xmlutil.TemplateElement('choices')
145 elem = xmlutil.SubTemplateElement(root, 'version', selector='choices')
146 make_version(elem)
147 return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
148
149
150class AtomSerializer(wsgi.XMLDictSerializer):
151
152 NSMAP = {None: xmlutil.XMLNS_ATOM}
153
154 def __init__(self, metadata=None, xmlns=None):
155 self.metadata = metadata or {}
156 if not xmlns:
157 self.xmlns = wsgi.XMLNS_ATOM
158 else:
159 self.xmlns = xmlns
160
161 def _get_most_recent_update(self, versions):
162 recent = None
163 for version in versions:
164 updated = datetime.datetime.strptime(version['updated'],
165 '%Y-%m-%dT%H:%M:%SZ')
166 if not recent:
167 recent = updated
168 elif updated > recent:
169 recent = updated
170
171 return recent.strftime('%Y-%m-%dT%H:%M:%SZ')
172
173 def _get_base_url(self, link_href):
174 # Make sure no trailing /
175 link_href = link_href.rstrip('/')
176 return link_href.rsplit('/', 1)[0] + '/'
177
178 def _create_feed(self, versions, feed_title, feed_id):
179 feed = etree.Element('feed', nsmap=self.NSMAP)
180 title = etree.SubElement(feed, 'title')
181 title.set('type', 'text')
182 title.text = feed_title
183
184 # Set this updated to the most recently updated version
185 recent = self._get_most_recent_update(versions)
186 etree.SubElement(feed, 'updated').text = recent
187
188 etree.SubElement(feed, 'id').text = feed_id
189
190 link = etree.SubElement(feed, 'link')
191 link.set('rel', 'self')
192 link.set('href', feed_id)
193
194 author = etree.SubElement(feed, 'author')
195 etree.SubElement(author, 'name').text = 'Rackspace'
196 etree.SubElement(author, 'uri').text = 'http://www.rackspace.com/'
197
198 for version in versions:
199 feed.append(self._create_version_entry(version))
200
201 return feed
202
203 def _create_version_entry(self, version):
204 entry = etree.Element('entry')
205 etree.SubElement(entry, 'id').text = version['links'][0]['href']
206 title = etree.SubElement(entry, 'title')
207 title.set('type', 'text')
208 title.text = 'Version %s' % version['id']
209 etree.SubElement(entry, 'updated').text = version['updated']
210
211 for link in version['links']:
212 link_elem = etree.SubElement(entry, 'link')
213 link_elem.set('rel', link['rel'])
214 link_elem.set('href', link['href'])
215 if 'type' in link:
216 link_elem.set('type', link['type'])
217
218 content = etree.SubElement(entry, 'content')
219 content.set('type', 'text')
220 content.text = 'Version %s %s (%s)' % (version['id'],
221 version['status'],
222 version['updated'])
223 return entry
224
225
226class VersionsAtomSerializer(AtomSerializer):
227 def default(self, data):
228 versions = data['versions']
229 feed_id = self._get_base_url(versions[0]['links'][0]['href'])
230 feed = self._create_feed(versions, 'Available API Versions', feed_id)
231 return self._to_xml(feed)
232
233
234class VersionAtomSerializer(AtomSerializer):
235 def default(self, data):
236 version = data['version']
237 feed_id = version['links'][0]['href']
238 feed = self._create_feed([version], 'About This Version', feed_id)
239 return self._to_xml(feed)
240
241
242class Versions(wsgi.Resource): 90class Versions(wsgi.Resource):
243 91
244 def __init__(self): 92 def __init__(self):
245 super(Versions, self).__init__(None) 93 super(Versions, self).__init__(None)
246 94
247 @wsgi.serializers(xml=VersionsTemplate,
248 atom=VersionsAtomSerializer)
249 def index(self, req): 95 def index(self, req):
250 """Return all versions.""" 96 """Return all versions."""
251 builder = views_versions.get_view_builder(req) 97 builder = views_versions.get_view_builder(req)
252 return builder.build_versions(get_supported_versions()) 98 return builder.build_versions(get_supported_versions())
253 99
254 @wsgi.serializers(xml=ChoicesTemplate)
255 @wsgi.response(300) 100 @wsgi.response(300)
256 def multi(self, req): 101 def multi(self, req):
257 """Return multiple choices.""" 102 """Return multiple choices."""
@@ -270,8 +115,6 @@ class Versions(wsgi.Resource):
270 115
271 116
272class ShareVersionV1(object): 117class ShareVersionV1(object):
273 @wsgi.serializers(xml=VersionTemplate,
274 atom=VersionAtomSerializer)
275 def show(self, req): 118 def show(self, req):
276 builder = views_versions.get_view_builder(req) 119 builder = views_versions.get_view_builder(req)
277 return builder.build_version(_KNOWN_VERSIONS['v1.0']) 120 return builder.build_version(_KNOWN_VERSIONS['v1.0'])
diff --git a/manila/api/xmlutil.py b/manila/api/xmlutil.py
deleted file mode 100644
index 2cc89d8..0000000
--- a/manila/api/xmlutil.py
+++ /dev/null
@@ -1,913 +0,0 @@
1# Copyright 2011 OpenStack LLC.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16import os.path
17
18from lxml import etree
19import six
20
21from manila.i18n import _
22from manila import utils
23
24
25XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
26XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
27XMLNS_COMMON_V10 = 'http://docs.openstack.org/common/api/v1.0'
28XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
29XMLNS_VOLUME_V1 = 'http://docs.openstack.org/volume/api/v1'
30XMLNS_VOLUME_V2 = ('http://docs.openstack.org/api/openstack-volume/2.0/'
31 'content')
32XMLNS_SHARE_V1 = ''
33
34
35def validate_schema(xml, schema_name):
36 if isinstance(xml, str):
37 xml = etree.fromstring(xml)
38 base_path = 'manila/api/schemas/v1.1/'
39 if schema_name in ('atom', 'atom-link'):
40 base_path = 'manila/api/schemas/'
41 schema_path = os.path.join(utils.maniladir(),
42 '%s%s.rng' % (base_path, schema_name))
43 schema_doc = etree.parse(schema_path)
44 relaxng = etree.RelaxNG(schema_doc)
45 relaxng.assertValid(xml)
46
47
48class Selector(object):
49 """Selects datum to operate on from an object."""
50
51 def __init__(self, *chain):
52 """Initialize the selector.
53
54 Each argument is a subsequent index into the object.
55 """
56
57 self.chain = chain
58
59 def __repr__(self):
60 """Return a representation of the selector."""
61
62 return "Selector" + repr(self.chain)
63
64 def __call__(self, obj, do_raise=False):
65 """Select a datum to operate on.
66
67 Selects the relevant datum within the object.
68
69 :param obj: The object from which to select the object.
70 :param do_raise: If False (the default), return None if the
71 indexed datum does not exist. Otherwise,
72 raise a KeyError.
73 """
74
75 # Walk the selector list
76 for elem in self.chain:
77 # If it's callable, call it
78 if callable(elem):
79 obj = elem(obj)
80 else:
81 # Use indexing
82 try:
83 obj = obj[elem]
84 except (KeyError, IndexError):
85 # No sense going any further
86 if do_raise:
87 # Convert to a KeyError, for consistency
88 raise KeyError(elem)
89 return None
90
91 # Return the finally-selected object
92 return obj
93
94
95def get_items(obj):
96 """Get items in obj."""
97
98 return list(obj.items())
99
100
101class EmptyStringSelector(Selector):
102 """Returns the empty string if Selector would return None."""
103 def __call__(self, obj, do_raise=False):
104 """Returns empty string if the selected value does not exist."""
105
106 try:
107 return super(EmptyStringSelector, self).__call__(obj, True)
108 except KeyError:
109 return ""
110
111
112class ConstantSelector(object):
113 """Returns a constant."""
114
115 def __init__(self, value):
116 """Initialize the selector.
117
118 :param value: The value to return.
119 """
120
121 self.value = value
122
123 def __repr__(self):
124 """Return a representation of the selector."""
125
126 return repr(self.value)
127
128 def __call__(self, _obj, _do_raise=False):
129 """Select a datum to operate on.
130
131 Returns a constant value. Compatible with
132 Selector.__call__().
133 """
134
135 return self.value
136
137
138class TemplateElement(object):
139 """Represent an element in the template."""
140
141 def __init__(self, tag, attrib=None, selector=None, subselector=None,
142 **extra):
143 """Initialize an element.
144
145 Initializes an element in the template. Keyword arguments
146 specify attributes to be set on the element; values must be
147 callables. See TemplateElement.set() for more information.
148
149 :param tag: The name of the tag to create.
150 :param attrib: An optional dictionary of element attributes.
151 :param selector: An optional callable taking an object and
152 optional boolean do_raise indicator and
153 returning the object bound to the element.
154 :param subselector: An optional callable taking an object and
155 optional boolean do_raise indicator and
156 returning the object bound to the element.
157 This is used to further refine the datum
158 object returned by selector in the event
159 that it is a list of objects.
160 """
161
162 # Convert selector into a Selector
163 if selector is None:
164 selector = Selector()
165 elif not callable(selector):
166 selector = Selector(selector)
167
168 # Convert subselector into a Selector
169 if subselector is not None and not callable(subselector):
170 subselector = Selector(subselector)
171
172 self.tag = tag
173 self.selector = selector
174 self.subselector = subselector
175 self.attrib = {}
176 self._text = None
177 self._children = []
178 self._childmap = {}
179
180 # Run the incoming attributes through set() so that they
181 # become selectorized
182 if not attrib:
183 attrib = {}
184 attrib.update(extra)
185 for k, v in attrib.items():
186 self.set(k, v)
187
188 def __repr__(self):
189 """Return a representation of the template element."""
190
191 return ('<%s.%s %r at %#x>' %
192 (self.__class__.__module__, self.__class__.__name__,
193 self.tag, id(self)))
194
195 def __len__(self):
196 """Return the number of child elements."""
197
198 return len(self._children)
199
200 def __contains__(self, key):
201 """Determine whether a child node named by key exists."""
202
203 return key in self._childmap
204
205 def __getitem__(self, idx):
206 """Retrieve a child node by index or name."""
207
208 if isinstance(idx, six.string_types):
209 # Allow access by node name
210 return self._childmap[idx]
211 else:
212 return self._children[idx]
213
214 def append(self, elem):
215 """Append a child to the element."""
216
217 # Unwrap templates...
218 elem = elem.unwrap()
219
220 # Avoid duplications
221 if elem.tag in self._childmap:
222 raise KeyError(elem.tag)
223
224 self._children.append(elem)
225 self._childmap[elem.tag] = elem
226
227 def extend(self, elems):
228 """Append children to the element."""
229
230 # Pre-evaluate the elements
231 elemmap = {}
232 elemlist = []
233 for elem in elems:
234 # Unwrap templates...
235 elem = elem.unwrap()
236
237 # Avoid duplications
238 if elem.tag in self._childmap or elem.tag in elemmap:
239 raise KeyError(elem.tag)
240
241 elemmap[elem.tag] = elem
242 elemlist.append(elem)
243
244 # Update the children
245 self._children.extend(elemlist)
246 self._childmap.update(elemmap)
247
248 def insert(self, idx, elem):
249 """Insert a child element at the given index."""
250
251 # Unwrap templates...
252 elem = elem.unwrap()
253
254 # Avoid duplications
255 if elem.tag in self._childmap:
256 raise KeyError(elem.tag)
257
258 self._children.insert(idx, elem)
259 self._childmap[elem.tag] = elem
260
261 def remove(self, elem):
262 """Remove a child element."""
263
264 # Unwrap templates...
265 elem = elem.unwrap()
266
267 # Check if element exists
268 if elem.tag not in self._childmap or self._childmap[elem.tag] != elem:
269 raise ValueError(_('element is not a child'))
270
271 self._children.remove(elem)
272 del self._childmap[elem.tag]
273
274 def get(self, key):
275 """Get an attribute.
276
277 Returns a callable which performs datum selection.
278
279 :param key: The name of the attribute to get.
280 """
281
282 return self.attrib[key]
283
284 def set(self, key, value=None):
285 """Set an attribute.
286
287 :param key: The name of the attribute to set.
288
289 :param value: A callable taking an object and optional boolean
290 do_raise indicator and returning the datum bound
291 to the attribute. If None, a Selector() will be
292 constructed from the key. If a string, a
293 Selector() will be constructed from the string.
294 """
295
296 # Convert value to a selector
297 if value is None:
298 value = Selector(key)
299 elif not callable(value):
300 value = Selector(value)
301
302 self.attrib[key] = value
303
304 def keys(self):
305 """Return the attribute names."""
306
307 return self.attrib.keys()
308
309 def items(self):
310 """Return the attribute names and values."""
311
312 return self.attrib.items()
313
314 def unwrap(self):
315 """Unwraps a template to return a template element."""
316
317 # We are a template element
318 return self
319
320 def wrap(self):
321 """Wraps a template element to return a template."""
322
323 # Wrap in a basic Template
324 return Template(self)
325
326 def apply(self, elem, obj):
327 """Apply text and attributes to an etree.Element.
328
329 Applies the text and attribute instructions in the template
330 element to an etree.Element instance.
331
332 :param elem: An etree.Element instance.
333 :param obj: The base object associated with this template
334 element.
335 """
336
337 # Start with the text...
338 if self.text is not None:
339 elem.text = six.text_type(self.text(obj))
340
341 # Now set up all the attributes...
342 for key, value in self.attrib.items():
343 try:
344 elem.set(key, six.text_type(value(obj, True)))
345 except KeyError:
346 # Attribute has no value, so don't include it
347 pass
348
349 def _render(self, parent, datum, patches, nsmap):
350 """Internal rendering.
351
352 Renders the template node into an etree.Element object.
353 Returns the etree.Element object.
354
355 :param parent: The parent etree.Element instance.
356 :param datum: The datum associated with this template element.
357 :param patches: A list of other template elements that must
358 also be applied.
359 :param nsmap: An optional namespace dictionary to be
360 associated with the etree.Element instance.
361 """
362
363 # Allocate a node
364 if callable(self.tag):
365 tagname = self.tag(datum)
366 else:
367 tagname = self.tag
368 elem = etree.Element(tagname, nsmap=nsmap)
369
370 # If we have a parent, append the node to the parent
371 if parent is not None:
372 parent.append(elem)
373
374 # If the datum is None, do nothing else
375 if datum is None:
376 return elem
377
378 # Apply this template element to the element
379 self.apply(elem, datum)
380
381 # Additionally, apply the patches
382 for patch in patches:
383 patch.apply(elem, datum)
384
385 # We have fully rendered the element; return it
386 return elem
387
388 def render(self, parent, obj, patches=[], nsmap=None):
389 """Render an object.
390
391 Renders an object against this template node. Returns a list
392 of two-item tuples, where the first item is an etree.Element
393 instance and the second item is the datum associated with that
394 instance.
395
396 :param parent: The parent for the etree.Element instances.
397 :param obj: The object to render this template element
398 against.
399 :param patches: A list of other template elements to apply
400 when rendering this template element.
401 :param nsmap: An optional namespace dictionary to attach to
402 the etree.Element instances.
403 """
404
405 # First, get the datum we're rendering
406 data = None if obj is None else self.selector(obj)
407
408 # Check if we should render at all
409 if not self.will_render(data):
410 return []
411 elif data is None:
412 return [(self._render(parent, None, patches, nsmap), None)]
413
414 # Make the data into a list if it isn't already
415 if not isinstance(data, list):
416 data = [data]
417 elif parent is None:
418 raise ValueError(_('root element selecting a list'))
419
420 # Render all the elements
421 elems = []
422 for datum in data:
423 if self.subselector is not None:
424 datum = self.subselector(datum)
425 elems.append((self._render(parent, datum, patches, nsmap), datum))
426
427 # Return all the elements rendered, as well as the
428 # corresponding datum for the next step down the tree
429 return elems
430
431 def will_render(self, datum):
432 """Hook method.
433
434 An overridable hook method to determine whether this template
435 element will be rendered at all. By default, returns False
436 (inhibiting rendering) if the datum is None.
437
438 :param datum: The datum associated with this template element.
439 """
440
441 # Don't render if datum is None
442 return datum is not None
443
444 def _text_get(self):
445 """Template element text.
446
447 Either None or a callable taking an object and optional
448 boolean do_raise indicator and returning the datum bound to
449 the text of the template element.
450 """
451
452 return self._text
453
454 def _text_set(self, value):
455 # Convert value to a selector
456 if value is not None and not callable(value):
457 value = Selector(value)
458
459 self._text = value
460
461 def _text_del(self):
462 self._text = None
463
464 text = property(_text_get, _text_set, _text_del)
465
466 def tree(self):
467 """Return string representation of the template tree.
468
469 Returns a representation of the template rooted at this
470 element as a string, suitable for inclusion in debug logs.
471 """
472
473 # Build the inner contents of the tag...
474 contents = [self.tag, '!selector=%r' % self.selector]
475
476 # Add the text...
477 if self.text is not None:
478 contents.append('!text=%r' % self.text)
479
480 # Add all the other attributes
481 for key, value in self.attrib.items():
482 contents.append('%s=%r' % (key, value))
483
484 # If there are no children, return it as a closed tag
485 if len(self) == 0:
486 return '<%s/>' % ' '.join([str(i) for i in contents])
487
488 # OK, recurse to our children
489 children = [c.tree() for c in self]
490
491 # Return the result
492 return ('<%s>%s</%s>' %
493 (' '.join(contents), ''.join(children), self.tag))
494
495
496def SubTemplateElement(parent, tag, attrib=None, selector=None,
497 subselector=None, **extra):
498 """Create a template element as a child of another.
499
500 Corresponds to the etree.SubElement interface. Parameters are as
501 for TemplateElement, with the addition of the parent.
502 """
503
504 # Convert attributes
505 attrib = attrib or {}
506 attrib.update(extra)
507
508 # Get a TemplateElement
509 elem = TemplateElement(tag, attrib=attrib, selector=selector,
510 subselector=subselector)
511
512 # Append the parent safely
513 if parent is not None:
514 parent.append(elem)
515
516 return elem
517
518
519class Template(object):
520 """Represent a template."""
521
522 def __init__(self, root, nsmap=None):
523 """Initialize a template.
524
525 :param root: The root element of the template.
526 :param nsmap: An optional namespace dictionary to be
527 associated with the root element of the
528 template.
529 """
530
531 self.root = root.unwrap() if root is not None else None
532 self.nsmap = nsmap or {}
533 self.serialize_options = dict(encoding='UTF-8', xml_declaration=True)
534
535 def _serialize(self, parent, obj, siblings, nsmap=None):
536 """Internal serialization.
537
538 Recursive routine to build a tree of etree.Element instances
539 from an object based on the template. Returns the first
540 etree.Element instance rendered, or None.
541
542 :param parent: The parent etree.Element instance. Can be
543 None.
544 :param obj: The object to render.
545 :param siblings: The TemplateElement instances against which
546 to render the object.
547 :param nsmap: An optional namespace dictionary to be
548 associated with the etree.Element instance
549 rendered.
550 """
551
552 # First step, render the element
553 elems = siblings[0].render(parent, obj, siblings[1:], nsmap)
554
555 # Now, recurse to all child elements
556 seen = set()
557 for idx, sibling in enumerate(siblings):
558 for child in sibling:
559 # Have we handled this child already?
560 if child.tag in seen:
561 continue
562 seen.add(child.tag)
563
564 # Determine the child's siblings
565 nieces = [child]
566 for sib in siblings[idx + 1:]:
567 if child.tag in sib:
568 nieces.append(sib[child.tag])
569
570 # Now we recurse for every data element
571 for elem, datum in elems:
572 self._serialize(elem, datum, nieces)
573
574 # Return the first element; at the top level, this will be the
575 # root element
576 if elems:
577 return elems[0][0]
578
579 def serialize(self, obj, *args, **kwargs):
580 """Serialize an object.
581
582 Serializes an object against the template. Returns a string
583 with the serialized XML. Positional and keyword arguments are
584 passed to etree.tostring().
585
586 :param obj: The object to serialize.
587 """
588
589 elem = self.make_tree(obj)
590 if elem is None:
591 return ''
592
593 for k, v in self.serialize_options.items():
594 kwargs.setdefault(k, v)
595
596 # Serialize it into XML
597 return etree.tostring(elem, *args, **kwargs)
598
599 def make_tree(self, obj):
600 """Create a tree.
601
602 Serializes an object against the template. Returns an Element
603 node with appropriate children.
604
605 :param obj: The object to serialize.
606 """
607
608 # If the template is empty, return the empty string
609 if self.root is None:
610 return None
611
612 # Get the siblings and nsmap of the root element
613 siblings = self._siblings()
614 nsmap = self._nsmap()
615
616 # Form the element tree
617 return self._serialize(None, obj, siblings, nsmap)
618
619 def _siblings(self):
620 """Hook method for computing root siblings.
621
622 An overridable hook method to return the siblings of the root
623 element. By default, this is the root element itself.
624 """
625
626 return [self.root]
627
628 def _nsmap(self):
629 """Hook method for computing the namespace dictionary.
630
631 An overridable hook method to return the namespace dictionary.
632 """
633
634 return self.nsmap.copy()
635
636 def unwrap(self):
637 """Unwraps a template to return a template element."""
638
639 # Return the root element
640 return self.root
641
642 def wrap(self):
643 """Wraps a template element to return a template."""
644
645 # We are a template
646 return self
647
648 def apply(self, master):
649 """Hook method for determining slave applicability.
650
651 An overridable hook method used to determine if this template
652 is applicable as a slave to a given master template.
653
654 :param master: The master template to test.
655 """
656
657 return True
658
659 def tree(self):
660 """Return string representation of the template tree.
661
662 Returns a representation of the template as a string, suitable
663 for inclusion in debug logs.
664 """
665
666 return "%r: %s" % (self, self.root.tree())
667
668
669class MasterTemplate(Template):
670 """Represent a master template.
671
672 Master templates are versioned derivatives of templates that
673 additionally allow slave templates to be attached. Slave
674 templates allow modification of the serialized result without
675 directly changing the master.
676 """
677
678 def __init__(self, root, version, nsmap=None):
679 """Initialize a master template.
680
681 :param root: The root element of the template.
682 :param version: The version number of the template.
683 :param nsmap: An optional namespace dictionary to be
684 associated with the root element of the
685 template.
686 """
687
688 super(MasterTemplate, self).__init__(root, nsmap)
689 self.version = version
690 self.slaves = []
691
692 def __repr__(self):
693 """Return string representation of the template."""
694
695 return ("<%s.%s object version %s at %#x>" %
696 (self.__class__.__module__, self.__class__.__name__,
697 self.version, id(self)))
698
699 def _siblings(self):
700 """Hook method for computing root siblings.
701
702 An overridable hook method to return the siblings of the root
703 element. This is the root element plus the root elements of
704 all the slave templates.
705 """
706
707 return [self.root] + [slave.root for slave in self.slaves]
708
709 def _nsmap(self):
710 """Hook method for computing the namespace dictionary.
711
712 An overridable hook method to return the namespace dictionary.
713 The namespace dictionary is computed by taking the master
714 template's namespace dictionary and updating it from all the
715 slave templates.
716 """
717
718 nsmap = self.nsmap.copy()
719 for slave in self.slaves:
720 nsmap.update(slave._nsmap())
721 return nsmap
722
723 def attach(self, *slaves):
724 """Attach one or more slave templates.
725
726 Attaches one or more slave templates to the master template.
727 Slave templates must have a root element with the same tag as
728 the master template. The slave template's apply() method will
729 be called to determine if the slave should be applied to this
730 master; if it returns False, that slave will be skipped.
731 (This allows filtering of slaves based on the version of the
732 master template.)
733 """
734
735 slave_list = []
736 for slave in slaves:
737 slave = slave.wrap()
738
739 # Make sure we have a tree match
740 if slave.root.tag != self.root.tag:
741 slavetag = slave.root.tag
742 mastertag = self.root.tag
743 msg = _("Template tree mismatch; adding slave %(slavetag)s "
744 "to master %(mastertag)s") % {
745 "slavetag": slavetag,
746 "mastertag": mastertag
747 }
748 raise ValueError(msg)
749
750 # Make sure slave applies to this template
751 if not slave.apply(self):
752 continue
753
754 slave_list.append(slave)
755
756 # Add the slaves
757 self.slaves.extend(slave_list)
758
759 def copy(self):
760 """Return a copy of this master template."""
761
762 # Return a copy of the MasterTemplate
763 tmp = self.__class__(self.root, self.version, self.nsmap)
764 tmp.slaves = self.slaves[:]
765 return tmp
766
767
768class SlaveTemplate(Template):
769 """Represent a slave template.
770
771 Slave templates are versioned derivatives of templates. Each
772 slave has a minimum version and optional maximum version of the
773 master template to which they can be attached.
774 """
775
776 def __init__(self, root, min_vers, max_vers=None, nsmap=None):
777 """Initialize a slave template.
778
779 :param root: The root element of the template.
780 :param min_vers: The minimum permissible version of the master
781 template for this slave template to apply.
782 :param max_vers: An optional upper bound for the master
783 template version.
784 :param nsmap: An optional namespace dictionary to be
785 associated with the root element of the
786 template.
787 """
788
789 super(SlaveTemplate, self).__init__(root, nsmap)
790 self.min_vers = min_vers
791 self.max_vers = max_vers
792
793 def __repr__(self):
794 """Return string representation of the template."""
795
796 return ("<%s.%s object versions %s-%s at %#x>" %
797 (self.__class__.__module__, self.__class__.__name__,
798 self.min_vers, self.max_vers, id(self)))
799
800 def apply(self, master):
801 """Hook method for determining slave applicability.
802
803 An overridable hook method used to determine if this template
804 is applicable as a slave to a given master template. This
805 version requires the master template to have a version number
806 between min_vers and max_vers.
807
808 :param master: The master template to test.
809 """
810
811 # Does the master meet our minimum version requirement?
812 if master.version < self.min_vers:
813 return False
814
815 # How about our maximum version requirement?
816 if self.max_vers is not None and master.version > self.max_vers:
817 return False
818
819 return True
820
821
822class TemplateBuilder(object):
823 """Template builder.
824
825 This class exists to allow templates to be lazily built without
826 having to build them each time they are needed. It must be
827 subclassed, and the subclass must implement the construct()
828 method, which must return a Template (or subclass) instance. The
829 constructor will always return the template returned by
830 construct(), or, if it has a copy() method, a copy of that
831 template.
832 """
833
834 _tmpl = None
835
836 def __new__(cls, copy=True):
837 """Construct and return a template.
838
839 :param copy: If True (the default), a copy of the template
840 will be constructed and returned, if possible.
841 """
842
843 # Do we need to construct the template?
844 if cls._tmpl is None:
845 tmp = super(TemplateBuilder, cls).__new__(cls)
846
847 # Construct the template
848 cls._tmpl = tmp.construct()
849
850 # If the template has a copy attribute, return the result of
851 # calling it
852 if copy and hasattr(cls._tmpl, 'copy'):
853 return cls._tmpl.copy()
854
855 # Return the template
856 return cls._tmpl
857
858 def construct(self):
859 """Construct a template.
860
861 Called to construct a template instance, which it must return.
862 Only called once.
863 """
864
865 raise NotImplementedError(_("subclasses must implement construct()!"))
866
867
868def make_links(parent, selector=None):
869 """Attach an Atom <links> element to the parent."""
870
871 elem = SubTemplateElement(parent, '{%s}link' % XMLNS_ATOM,
872 selector=selector)
873 elem.set('rel')
874 elem.set('type')
875 elem.set('href')
876
877 # Just for completeness...
878 return elem
879
880
881def make_flat_dict(name, selector=None, subselector=None, ns=None):
882 """Utility for simple XML templates.
883
884 Utility for simple XML templates that traditionally used
885 XMLDictSerializer with no metadata. Returns a template element
886 where the top-level element has the given tag name, and where
887 sub-elements have tag names derived from the object's keys and
888 text derived from the object's values. This only works for flat
889 dictionary objects, not dictionaries containing nested lists or
890 dictionaries.
891 """
892
893 # Set up the names we need...
894 if ns is None:
895 elemname = name
896 tagname = Selector(0)
897 else:
898 elemname = '{%s}%s' % (ns, name)
899 tagname = lambda obj, do_raise=False: '{%s}%s' % (ns, obj[0])
900
901 if selector is None:
902 selector = name
903
904 # Build the root element
905 root = TemplateElement(elemname, selector=selector,
906 subselector=subselector)
907
908 # Build an element to represent all the keys and values
909 elem = SubTemplateElement(root, tagname, selector=get_items)
910 elem.text = 1
911
912 # Return the template
913 return root
diff --git a/manila/tests/api/middleware/test_faults.py b/manila/tests/api/middleware/test_faults.py
index ba75bc4..db80295 100644
--- a/manila/tests/api/middleware/test_faults.py
+++ b/manila/tests/api/middleware/test_faults.py
@@ -13,14 +13,11 @@
13# License for the specific language governing permissions and limitations 13# License for the specific language governing permissions and limitations
14# under the License. 14# under the License.
15 15
16from xml.dom import minidom
17
18from oslo_serialization import jsonutils 16from oslo_serialization import jsonutils
19import webob 17import webob
20import webob.dec 18import webob.dec
21import webob.exc 19import webob.exc
22 20
23from manila.api import common
24from manila.api.openstack import wsgi 21from manila.api.openstack import wsgi
25from manila import test 22from manila import test
26 23
@@ -88,9 +85,9 @@ class TestFaults(test.TestCase):
88 def raiser(req): 85 def raiser(req):
89 raise wsgi.Fault(webob.exc.HTTPNotFound(explanation='whut?')) 86 raise wsgi.Fault(webob.exc.HTTPNotFound(explanation='whut?'))
90 87
91 req = webob.Request.blank('/.xml') 88 req = webob.Request.blank('/.json')
92 resp = req.get_response(raiser) 89 resp = req.get_response(raiser)
93 self.assertEqual(resp.content_type, "application/xml") 90 self.assertEqual(resp.content_type, "application/json")
94 self.assertEqual(resp.status_int, 404) 91 self.assertEqual(resp.status_int, 404)
95 self.assertTrue('whut?' in resp.body) 92 self.assertTrue('whut?' in resp.body)
96 93
@@ -100,9 +97,9 @@ class TestFaults(test.TestCase):
100 def raiser(req): 97 def raiser(req):
101 raise wsgi.Fault(webob.exc.HTTPForbidden(explanation='whut?')) 98 raise wsgi.Fault(webob.exc.HTTPForbidden(explanation='whut?'))
102 99
103 req = webob.Request.blank('/.xml') 100 req = webob.Request.blank('/.json')
104 resp = req.get_response(raiser) 101 resp = req.get_response(raiser)
105 self.assertEqual(resp.content_type, "application/xml") 102 self.assertEqual(resp.content_type, "application/json")
106 self.assertEqual(resp.status_int, 403) 103 self.assertEqual(resp.status_int, 403)
107 self.assertTrue('resizeNotAllowed' not in resp.body) 104 self.assertTrue('resizeNotAllowed' not in resp.body)
108 self.assertTrue('forbidden' in resp.body) 105 self.assertTrue('forbidden' in resp.body)
@@ -111,104 +108,3 @@ class TestFaults(test.TestCase):
111 """Ensure the status_int is set correctly on faults.""" 108 """Ensure the status_int is set correctly on faults."""
112 fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='what?')) 109 fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='what?'))
113 self.assertEqual(fault.status_int, 400) 110 self.assertEqual(fault.status_int, 400)
114
115 def test_xml_serializer(self):
116 """Ensure that a v1.1 request responds with a v1 xmlns."""
117 request = webob.Request.blank('/v1',
118 headers={"Accept": "application/xml"})
119
120 fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='scram'))
121 response = request.get_response(fault)
122
123 self.assertTrue(common.XML_NS_V1 in response.body)
124 self.assertEqual(response.content_type, "application/xml")
125 self.assertEqual(response.status_int, 400)
126
127
128class FaultsXMLSerializationTestV11(test.TestCase):
129 """Tests covering `manila.api.openstack.faults:Fault` class."""
130
131 def _prepare_xml(self, xml_string):
132 xml_string = xml_string.replace(" ", "")
133 xml_string = xml_string.replace("\n", "")
134 xml_string = xml_string.replace("\t", "")
135 return xml_string
136
137 def test_400_fault(self):
138 metadata = {'attributes': {"badRequest": 'code'}}
139 serializer = wsgi.XMLDictSerializer(metadata=metadata,
140 xmlns=common.XML_NS_V1)
141
142 fixture = {
143 "badRequest": {
144 "message": "scram",
145 "code": 400,
146 },
147 }
148
149 output = serializer.serialize(fixture)
150 actual = minidom.parseString(self._prepare_xml(output))
151
152 expected = minidom.parseString(self._prepare_xml("""
153 <badRequest code="400" xmlns="%s">
154 <message>scram</message>
155 </badRequest>
156 """) % common.XML_NS_V1)
157
158 self.assertEqual(expected.toxml(), actual.toxml())
159
160 def test_413_fault(self):
161 metadata = {'attributes': {"overLimit": 'code'}}
162 serializer = wsgi.XMLDictSerializer(metadata=metadata,
163 xmlns=common.XML_NS_V1)
164
165 fixture = {
166 "overLimit": {
167 "message": "sorry",
168 "code": 413,
169 "retryAfter": 4,
170 },
171 }
172
173 output = serializer.serialize(fixture)
174 result = minidom.parseString(self._prepare_xml(output))
175
176 # result has 1 child - overLimit
177 self.assertEqual(result.firstChild, result.lastChild)
178 self.assertEqual(result.firstChild.tagName, 'overLimit')
179
180 # overLimit has attrs code = '413' and xmlns = common.XML_NS_V1
181 self.assertEqual(result.firstChild.getAttribute('code'), '413')
182 self.assertEqual(result.firstChild.getAttribute('xmlns'),
183 common.XML_NS_V1)
184
185 # overLimit has childs message = 'sorry' and retryAfter = '4'
186 message = result.firstChild.getElementsByTagName('message')
187 retry_after = result.firstChild.getElementsByTagName('retryAfter')
188 self.assertEqual(len(message), 1)
189 self.assertEqual(len(retry_after), 1)
190 self.assertEqual(message[0].toxml(), '<message>sorry</message>')
191 self.assertEqual(retry_after[0].toxml(), '<retryAfter>4</retryAfter>')
192
193 def test_404_fault(self):
194 metadata = {'attributes': {"itemNotFound": 'code'}}
195 serializer = wsgi.XMLDictSerializer(metadata=metadata,
196 xmlns=common.XML_NS_V1)
197
198 fixture = {
199 "itemNotFound": {
200 "message": "sorry",
201 "code": 404,
202 },
203 }
204
205 output = serializer.serialize(fixture)
206 actual = minidom.parseString(self._prepare_xml(output))
207
208 expected = minidom.parseString(self._prepare_xml("""
209 <itemNotFound code="404" xmlns="%s">
210 <message>sorry</message>
211 </itemNotFound>
212 """) % common.XML_NS_V1)
213
214 self.assertEqual(expected.toxml(), actual.toxml())
diff --git a/manila/tests/api/openstack/test_wsgi.py b/manila/tests/api/openstack/test_wsgi.py
index d35d877..788defb 100644
--- a/manila/tests/api/openstack/test_wsgi.py
+++ b/manila/tests/api/openstack/test_wsgi.py
@@ -29,14 +29,13 @@ class RequestTest(test.TestCase):
29 self.assertEqual(result, "application/json") 29 self.assertEqual(result, "application/json")
30 30
31 def test_content_type_from_accept(self): 31 def test_content_type_from_accept(self):
32 for content_type in ('application/xml', 32 content_type = 'application/json'
33 'application/vnd.openstack.volume+xml', 33 request = wsgi.Request.blank('/tests/123')
34 'application/json', 34 request.headers["Accept"] = content_type
35 'application/vnd.openstack.volume+json'): 35
36 request = wsgi.Request.blank('/tests/123') 36 result = request.best_match_content_type()
37 request.headers["Accept"] = content_type 37
38 result = request.best_match_content_type() 38 self.assertEqual(result, content_type)
39 self.assertEqual(result, content_type)
40 39
41 def test_content_type_from_accept_best(self): 40 def test_content_type_from_accept_best(self):
42 request = wsgi.Request.blank('/tests/123') 41 request = wsgi.Request.blank('/tests/123')
@@ -48,13 +47,9 @@ class RequestTest(test.TestCase):
48 request.headers["Accept"] = ("application/json; q=0.3, " 47 request.headers["Accept"] = ("application/json; q=0.3, "
49 "application/xml; q=0.9") 48 "application/xml; q=0.9")
50 result = request.best_match_content_type() 49 result = request.best_match_content_type()
51 self.assertEqual(result, "application/xml") 50 self.assertEqual(result, "application/json")
52 51
53 def test_content_type_from_query_extension(self): 52 def test_content_type_from_query_extension(self):
54 request = wsgi.Request.blank('/tests/123.xml')
55 result = request.best_match_content_type()
56 self.assertEqual(result, "application/xml")
57
58 request = wsgi.Request.blank('/tests/123.json') 53 request = wsgi.Request.blank('/tests/123.json')
59 result = request.best_match_content_type() 54 result = request.best_match_content_type()
60 self.assertEqual(result, "application/json") 55 self.assertEqual(result, "application/json")
@@ -63,12 +58,6 @@ class RequestTest(test.TestCase):
63 result = request.best_match_content_type() 58 result = request.best_match_content_type()
64 self.assertEqual(result, "application/json") 59 self.assertEqual(result, "application/json")
65 60
66 def test_content_type_accept_and_query_extension(self):
67 request = wsgi.Request.blank('/tests/123.xml')
68 request.headers["Accept"] = "application/json"
69 result = request.best_match_content_type()
70 self.assertEqual(result, "application/xml")
71
72 def test_content_type_accept_default(self): 61 def test_content_type_accept_default(self):
73 request = wsgi.Request.blank('/tests/123.unsupported') 62 request = wsgi.Request.blank('/tests/123.unsupported')
74 request.headers["Accept"] = "application/unsupported1" 63 request.headers["Accept"] = "application/unsupported1"
@@ -162,16 +151,6 @@ class DictSerializerTest(test.TestCase):
162 self.assertEqual(serializer.serialize({}, 'update'), '') 151 self.assertEqual(serializer.serialize({}, 'update'), '')
163 152
164 153
165class XMLDictSerializerTest(test.TestCase):
166 def test_xml(self):
167 input_dict = dict(servers=dict(a=(2, 3)))
168 expected_xml = '<serversxmlns="asdf"><a>(2,3)</a></servers>'
169 serializer = wsgi.XMLDictSerializer(xmlns="asdf")
170 result = serializer.serialize(input_dict)
171 result = result.replace('\n', '').replace(' ', '')
172 self.assertEqual(result, expected_xml)
173
174
175class JSONDictSerializerTest(test.TestCase): 154class JSONDictSerializerTest(test.TestCase):
176 def test_json(self): 155 def test_json(self):
177 input_dict = dict(servers=dict(a=(2, 3))) 156 input_dict = dict(servers=dict(a=(2, 3)))
@@ -211,37 +190,6 @@ class JSONDeserializerTest(test.TestCase):
211 self.assertEqual(deserializer.deserialize(data), as_dict) 190 self.assertEqual(deserializer.deserialize(data), as_dict)
212 191
213 192
214class XMLDeserializerTest(test.TestCase):
215 def test_xml(self):
216 xml = """
217 <a a1="1" a2="2">
218 <bs><b>1</b><b>2</b><b>3</b><b><c c1="1"/></b></bs>
219 <d><e>1</e></d>
220 <f>1</f>
221 </a>
222 """.strip()
223 as_dict = {
224 'body': {
225 'a': {
226 'a1': '1',
227 'a2': '2',
228 'bs': ['1', '2', '3', {'c': {'c1': '1'}}],
229 'd': {'e': '1'},
230 'f': '1',
231 },
232 },
233 }
234 metadata = {'plurals': {'bs': 'b', 'ts': 't'}}
235 deserializer = wsgi.XMLDeserializer(metadata=metadata)
236 self.assertEqual(deserializer.deserialize(xml), as_dict)
237
238 def test_xml_empty(self):
239 xml = """<a></a>"""
240 as_dict = {"body": {"a": {}}}
241 deserializer = wsgi.XMLDeserializer()
242 self.assertEqual(deserializer.deserialize(xml), as_dict)
243
244
245class ResourceTest(test.TestCase): 193class ResourceTest(test.TestCase):
246 def test_resource_call(self): 194 def test_resource_call(self):
247 class Controller(object): 195 class Controller(object):
@@ -299,19 +247,6 @@ class ResourceTest(test.TestCase):
299 '{"fooAction": true}') 247 '{"fooAction": true}')
300 self.assertEqual(controller._action_foo, method) 248 self.assertEqual(controller._action_foo, method)
301 249
302 def test_get_method_action_xml(self):
303 class Controller(wsgi.Controller):
304 @wsgi.action('fooAction')
305 def _action_foo(self, req, id, body):
306 return body
307
308 controller = Controller()
309 resource = wsgi.Resource(controller)
310 method, extensions = resource.get_method(None, 'action',
311 'application/xml',
312 '<fooAction>true</fooAction>')
313 self.assertEqual(controller._action_foo, method)
314
315 def test_get_method_action_bad_body(self): 250 def test_get_method_action_bad_body(self):
316 class Controller(wsgi.Controller): 251 class Controller(wsgi.Controller):
317 @wsgi.action('fooAction') 252 @wsgi.action('fooAction')
@@ -467,20 +402,15 @@ class ResourceTest(test.TestCase):
467 def deserialize(self, body): 402 def deserialize(self, body):
468 return 'json' 403 return 'json'
469 404
470 class XMLDeserializer(object):
471 def deserialize(self, body):
472 return 'xml'
473
474 class Controller(object): 405 class Controller(object):
475 @wsgi.deserializers(xml=XMLDeserializer)
476 def index(self, req, pants=None): 406 def index(self, req, pants=None):
477 return pants 407 return pants
478 408
479 controller = Controller() 409 controller = Controller()
480 resource = wsgi.Resource(controller, json=JSONDeserializer) 410 resource = wsgi.Resource(controller, json=JSONDeserializer)
481 411
482 obj = resource.deserialize(controller.index, 'application/xml', 'foo') 412 obj = resource.deserialize(controller.index, 'application/json', 'foo')
483 self.assertEqual(obj, 'xml') 413 self.assertEqual(obj, 'json')
484 414
485 def test_register_actions(self): 415 def test_register_actions(self):
486 class Controller(object): 416 class Controller(object):
diff --git a/manila/tests/api/test_extensions.py b/manila/tests/api/test_extensions.py
index e1acf4b..c478f77 100644
--- a/manila/tests/api/test_extensions.py
+++ b/manila/tests/api/test_extensions.py
@@ -16,7 +16,6 @@
16 16
17import ddt 17import ddt
18import iso8601 18import iso8601
19from lxml import etree
20import mock 19import mock
21from oslo_config import cfg 20from oslo_config import cfg
22from oslo_serialization import jsonutils 21from oslo_serialization import jsonutils
@@ -24,7 +23,6 @@ import webob
24 23
25from manila.api import extensions 24from manila.api import extensions
26from manila.api.v1 import router 25from manila.api.v1 import router
27from manila.api import xmlutil
28from manila import policy 26from manila import policy
29from manila import test 27from manila import test
30 28
@@ -70,8 +68,7 @@ class ExtensionControllerTest(ExtensionTestCase):
70 (fox_ext, ) = [ 68 (fox_ext, ) = [
71 x for x in data['extensions'] if x['alias'] == 'FOXNSOX'] 69 x for x in data['extensions'] if x['alias'] == 'FOXNSOX']
72 self.assertEqual( 70 self.assertEqual(
73 fox_ext, {'namespace': 'http://www.fox.in.socks/api/ext/pie/v1.0', 71 fox_ext, {'name': 'Fox In Socks',
74 'name': 'Fox In Socks',
75 'updated': '2011-01-22T13:25:27-06:00', 72 'updated': '2011-01-22T13:25:27-06:00',
76 'description': 'The Fox In Socks Extension.', 73 'description': 'The Fox In Socks Extension.',
77 'alias': 'FOXNSOX', 74 'alias': 'FOXNSOX',
@@ -93,8 +90,7 @@ class ExtensionControllerTest(ExtensionTestCase):
93 data = jsonutils.loads(response.body) 90 data = jsonutils.loads(response.body)
94 self.assertEqual( 91 self.assertEqual(
95 data['extension'], 92 data['extension'],
96 {"namespace": "http://www.fox.in.socks/api/ext/pie/v1.0", 93 {"name": "Fox In Socks",
97 "name": "Fox In Socks",
98 "updated": "2011-01-22T13:25:27-06:00", 94 "updated": "2011-01-22T13:25:27-06:00",
99 "description": "The Fox In Socks Extension.", 95 "description": "The Fox In Socks Extension.",
100 "alias": "FOXNSOX", 96 "alias": "FOXNSOX",
@@ -106,55 +102,6 @@ class ExtensionControllerTest(ExtensionTestCase):
106 response = request.get_response(app) 102 response = request.get_response(app)
107 self.assertEqual(404, response.status_int) 103 self.assertEqual(404, response.status_int)
108 104
109 def test_list_extensions_xml(self):
110 app = router.APIRouter()
111 request = webob.Request.blank("/fake/extensions")
112 request.accept = "application/xml"
113 response = request.get_response(app)
114 self.assertEqual(200, response.status_int)
115
116 root = etree.XML(response.body)
117 self.assertEqual(root.tag.split('extensions')[0], NS)
118
119 # Make sure we have all the extensions, extras extensions being OK.
120 exts = root.findall('{0}extension'.format(NS))
121 self.assertTrue(len(exts) >= len(self.ext_list))
122
123 # Make sure that at least Fox in Sox is correct.
124 (fox_ext, ) = [x for x in exts if x.get('alias') == 'FOXNSOX']
125 self.assertEqual(fox_ext.get('name'), 'Fox In Socks')
126 self.assertEqual(
127 fox_ext.get('namespace'),
128 'http://www.fox.in.socks/api/ext/pie/v1.0')
129 self.assertEqual(fox_ext.get('updated'), '2011-01-22T13:25:27-06:00')
130 self.assertEqual(
131 fox_ext.findtext('{0}description'.format(NS)),
132 'The Fox In Socks Extension.')
133
134 xmlutil.validate_schema(root, 'extensions')
135
136 def test_get_extension_xml(self):
137 app = router.APIRouter()
138 request = webob.Request.blank("/fake/extensions/FOXNSOX")
139 request.accept = "application/xml"
140 response = request.get_response(app)
141 self.assertEqual(200, response.status_int)
142 xml = response.body
143
144 root = etree.XML(xml)
145 self.assertEqual(root.tag.split('extension')[0], NS)
146 self.assertEqual(root.get('alias'), 'FOXNSOX')
147 self.assertEqual(root.get('name'), 'Fox In Socks')
148 self.assertEqual(
149 root.get('namespace'),
150 'http://www.fox.in.socks/api/ext/pie/v1.0')
151 self.assertEqual(root.get('updated'), '2011-01-22T13:25:27-06:00')
152 self.assertEqual(
153 root.findtext('{0}description'.format(NS)),
154 'The Fox In Socks Extension.')
155
156 xmlutil.validate_schema(root, 'extension')
157
158 105
159@ddt.ddt 106@ddt.ddt
160class ExtensionAuthorizeTestCase(test.TestCase): 107class ExtensionAuthorizeTestCase(test.TestCase):
diff --git a/manila/tests/api/test_xmlutil.py b/manila/tests/api/test_xmlutil.py
deleted file mode 100644
index 11eca14..0000000
--- a/manila/tests/api/test_xmlutil.py
+++ /dev/null
@@ -1,714 +0,0 @@
1# Copyright 2011 OpenStack LLC.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16from xml.dom import minidom
17
18from lxml import etree
19
20from manila.api import xmlutil
21from manila import test
22
23
24class SelectorTest(test.TestCase):
25 obj_for_test = {'test': {'name': 'test',
26 'values': [1, 2, 3],
27 'attrs': {'foo': 1,
28 'bar': 2,
29 'baz': 3, }, }, }
30
31 def test_empty_selector(self):
32 sel = xmlutil.Selector()
33 self.assertEqual(len(sel.chain), 0)
34 self.assertEqual(sel(self.obj_for_test), self.obj_for_test)
35
36 def test_dict_selector(self):
37 sel = xmlutil.Selector('test')
38 self.assertEqual(len(sel.chain), 1)
39 self.assertEqual(sel.chain[0], 'test')
40 self.assertEqual(sel(self.obj_for_test),
41 self.obj_for_test['test'])
42
43 def test_datum_selector(self):
44 sel = xmlutil.Selector('test', 'name')
45 self.assertEqual(len(sel.chain), 2)
46 self.assertEqual(sel.chain[0], 'test')
47 self.assertEqual(sel.chain[1], 'name')
48 self.assertEqual(sel(self.obj_for_test), 'test')
49
50 def test_list_selector(self):
51 sel = xmlutil.Selector('test', 'values', 0)
52 self.assertEqual(len(sel.chain), 3)
53 self.assertEqual(sel.chain[0], 'test')
54 self.assertEqual(sel.chain[1], 'values')
55 self.assertEqual(sel.chain[2], 0)
56 self.assertEqual(sel(self.obj_for_test), 1)
57
58 def test_items_selector(self):
59 sel = xmlutil.Selector('test', 'attrs', xmlutil.get_items)
60 self.assertEqual(len(sel.chain), 3)
61 self.assertEqual(sel.chain[2], xmlutil.get_items)
62 for key, val in sel(self.obj_for_test):
63 self.assertEqual(self.obj_for_test['test']['attrs'][key], val)
64
65 def test_missing_key_selector(self):
66 sel = xmlutil.Selector('test2', 'attrs')
67 self.assertEqual(sel(self.obj_for_test), None)
68 self.assertRaises(KeyError, sel, self.obj_for_test, True)
69
70 def test_constant_selector(self):
71 sel = xmlutil.ConstantSelector('Foobar')
72 self.assertEqual(sel.value, 'Foobar')
73 self.assertEqual(sel(self.obj_for_test), 'Foobar')
74
75
76class TemplateElementTest(test.TestCase):
77 def test_element_initial_attributes(self):
78 # Create a template element with some attributes
79 elem = xmlutil.TemplateElement('test', attrib=dict(a=1, b=2, c=3),
80 c=4, d=5, e=6)
81
82 # Verify all the attributes are as expected
83 expected = dict(a=1, b=2, c=4, d=5, e=6)
84 for k, v in expected.items():
85 self.assertEqual(elem.attrib[k].chain[0], v)
86
87 def test_element_get_attributes(self):
88 expected = dict(a=1, b=2, c=3)
89
90 # Create a template element with some attributes
91 elem = xmlutil.TemplateElement('test', attrib=expected)
92
93 # Verify that get() retrieves the attributes
94 for k, v in expected.items():
95 self.assertEqual(elem.get(k).chain[0], v)
96
97 def test_element_set_attributes(self):
98 attrs = dict(a=None, b='foo', c=xmlutil.Selector('foo', 'bar'))
99
100 # Create a bare template element with no attributes
101 elem = xmlutil.TemplateElement('test')
102
103 # Set the attribute values
104 for k, v in attrs.items():
105 elem.set(k, v)
106
107 # Now verify what got set
108 self.assertEqual(len(elem.attrib['a'].chain), 1)
109 self.assertEqual(elem.attrib['a'].chain[0], 'a')
110 self.assertEqual(len(elem.attrib['b'].chain), 1)
111 self.assertEqual(elem.attrib['b'].chain[0], 'foo')
112 self.assertEqual(elem.attrib['c'], attrs['c'])
113
114 def test_element_attribute_keys(self):
115 attrs = dict(a=1, b=2, c=3, d=4)
116 expected = set(attrs.keys())
117
118 # Create a template element with some attributes
119 elem = xmlutil.TemplateElement('test', attrib=attrs)
120
121 # Now verify keys
122 self.assertEqual(set(elem.keys()), expected)
123
124 def test_element_attribute_items(self):
125 expected = dict(a=xmlutil.Selector(1),
126 b=xmlutil.Selector(2),
127 c=xmlutil.Selector(3))
128 keys = set(expected.keys())
129
130 # Create a template element with some attributes
131 elem = xmlutil.TemplateElement('test', attrib=expected)
132
133 # Now verify items
134 for k, v in elem.items():
135 self.assertEqual(expected[k], v)
136 keys.remove(k)
137
138 # Did we visit all keys?
139 self.assertEqual(len(keys), 0)
140
141 def test_element_selector_none(self):
142 # Create a template element with no selector
143 elem = xmlutil.TemplateElement('test')
144
145 self.assertEqual(len(elem.selector.chain), 0)
146
147 def test_element_selector_string(self):
148 # Create a template element with a string selector
149 elem = xmlutil.TemplateElement('test', selector='test')
150
151 self.assertEqual(len(elem.selector.chain), 1)
152 self.assertEqual(elem.selector.chain[0], 'test')
153
154 def test_element_selector(self):
155 sel = xmlutil.Selector('a', 'b')
156
157 # Create a template element with an explicit selector
158 elem = xmlutil.TemplateElement('test', selector=sel)
159
160 self.assertEqual(elem.selector, sel)
161
162 def test_element_subselector_none(self):
163 # Create a template element with no subselector
164 elem = xmlutil.TemplateElement('test')
165
166 self.assertEqual(elem.subselector, None)
167
168 def test_element_subselector_string(self):