Browse Source

Merge "Remove Limited XML API Support from Manila"

Jenkins 4 years ago
parent
commit
61475eb5b6
39 changed files with 21 additions and 3180 deletions
  1. 1
    1
      contrib/tempest/tempest/api/share/test_extensions.py
  2. 0
    76
      manila/api/common.py
  3. 0
    1
      manila/api/contrib/admin_actions.py
  4. 0
    2
      manila/api/contrib/extended_quotas.py
  5. 0
    22
      manila/api/contrib/quota_classes.py
  6. 0
    18
      manila/api/contrib/quotas.py
  7. 0
    30
      manila/api/contrib/services.py
  8. 0
    39
      manila/api/contrib/share_actions.py
  9. 0
    2
      manila/api/contrib/share_manage.py
  10. 0
    49
      manila/api/contrib/share_type_access.py
  11. 0
    2
      manila/api/contrib/share_unmanage.py
  12. 0
    1
      manila/api/contrib/types_extra_specs.py
  13. 0
    1
      manila/api/contrib/types_manage.py
  14. 0
    1
      manila/api/contrib/used_limits.py
  15. 0
    2
      manila/api/contrib/user_quotas.py
  16. 0
    58
      manila/api/extensions.py
  17. 3
    236
      manila/api/openstack/wsgi.py
  18. 0
    141
      manila/api/schemas/atom-link.rng
  19. 0
    11
      manila/api/schemas/v1.1/extension.rng
  20. 0
    6
      manila/api/schemas/v1.1/extensions.rng
  21. 0
    28
      manila/api/schemas/v1.1/limits.rng
  22. 0
    9
      manila/api/schemas/v1.1/metadata.rng
  23. 1
    9
      manila/api/urlmap.py
  24. 0
    30
      manila/api/v1/limits.py
  25. 0
    30
      manila/api/v1/security_service.py
  26. 0
    9
      manila/api/v1/share_metadata.py
  27. 0
    45
      manila/api/v1/share_networks.py
  28. 0
    34
      manila/api/v1/share_servers.py
  29. 0
    45
      manila/api/v1/share_snapshots.py
  30. 0
    32
      manila/api/v1/shares.py
  31. 0
    157
      manila/api/versions.py
  32. 0
    913
      manila/api/xmlutil.py
  33. 4
    108
      manila/tests/api/middleware/test_faults.py
  34. 10
    80
      manila/tests/api/openstack/test_wsgi.py
  35. 2
    55
      manila/tests/api/test_extensions.py
  36. 0
    714
      manila/tests/api/test_xmlutil.py
  37. 0
    109
      manila/tests/api/v1/test_limits.py
  38. 0
    30
      manila/tests/test_utils.py
  39. 0
    44
      manila/utils.py

+ 1
- 1
contrib/tempest/tempest/api/share/test_extensions.py View File

@@ -27,5 +27,5 @@ class ExtensionsTest(base.BaseSharesTest):
27 27
 
28 28
         # verify response
29 29
         self.assertIn(int(resp["status"]), self.HTTP_SUCCESS)
30
-        keys = ["alias", "updated", "namespace", "name", "description"]
30
+        keys = ["alias", "updated", "name", "description"]
31 31
         [self.assertIn(key, ext.keys()) for ext in extensions for key in keys]

+ 0
- 76
manila/api/common.py View File

@@ -22,10 +22,7 @@ import six
22 22
 from six.moves.urllib import parse
23 23
 import webob
24 24
 
25
-from manila.api.openstack import wsgi
26
-from manila.api import xmlutil
27 25
 from manila.i18n import _
28
-from manila import utils
29 26
 
30 27
 api_common_opts = [
31 28
     cfg.IntOpt(
@@ -41,7 +38,6 @@ api_common_opts = [
41 38
 CONF = cfg.CONF
42 39
 CONF.register_opts(api_common_opts)
43 40
 LOG = log.getLogger(__name__)
44
-XML_NS_V1 = 'http://docs.openstack.org/volume/api/v1'
45 41
 
46 42
 
47 43
 # Regex that matches alphanumeric characters, periods, hypens,
@@ -267,78 +263,6 @@ class ViewBuilder(object):
267 263
         return parse.urlunsplit(url_parts)
268 264
 
269 265
 
270
-class 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
-
278
-class 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
-
285
-class 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
-
315
-metadata_nsmap = {None: xmlutil.XMLNS_V11}
316
-
317
-
318
-class 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
-
327
-class MetadataTemplateElement(xmlutil.TemplateElement):
328
-    def will_render(self, datum):
329
-        return True
330
-
331
-
332
-class 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
-
342 266
 def remove_invalid_options(context, search_options, allowed_search_options):
343 267
     """Remove search options that are not valid for non-admin API/context."""
344 268
     if context.is_admin:

+ 0
- 1
manila/api/contrib/admin_actions.py View File

@@ -131,7 +131,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
131 131
 
132 132
     name = "AdminActions"
133 133
     alias = "os-admin-actions"
134
-    namespace = "http://docs.openstack.org/share/ext/admin-actions/api/v1.1"
135 134
     updated = "2012-08-25T00:00:00+00:00"
136 135
 
137 136
     def get_controller_extensions(self):

+ 0
- 2
manila/api/contrib/extended_quotas.py View File

@@ -25,6 +25,4 @@ class Extended_quotas(extensions.ExtensionDescriptor):
25 25
 
26 26
     name = "ExtendedQuotas"
27 27
     alias = "os-extended-quotas"
28
-    namespace = ("http://docs.openstack.org/compute/ext/extended_quotas"
29
-                 "/api/v1.1")
30 28
     updated = "2013-06-09T00:00:00+00:00"

+ 0
- 22
manila/api/contrib/quota_classes.py View File

@@ -16,32 +16,14 @@
16 16
 import webob
17 17
 
18 18
 from manila.api import extensions
19
-from manila.api.openstack import wsgi
20
-from manila.api import xmlutil
21 19
 from manila import db
22 20
 from manila import exception
23 21
 from manila import quota
24 22
 
25
-
26 23
 QUOTAS = quota.QUOTAS
27
-
28
-
29 24
 authorize = extensions.extension_authorizer('share', 'quota_classes')
30 25
 
31 26
 
32
-class 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
-
45 27
 class QuotaClassSetsController(object):
46 28
 
47 29
     def _format_quota_set(self, quota_class, quota_set):
@@ -54,7 +36,6 @@ class QuotaClassSetsController(object):
54 36
 
55 37
         return dict(quota_class_set=result)
56 38
 
57
-    @wsgi.serializers(xml=QuotaClassTemplate)
58 39
     def show(self, req, id):
59 40
         context = req.environ['manila.context']
60 41
         authorize(context)
@@ -66,7 +47,6 @@ class QuotaClassSetsController(object):
66 47
         return self._format_quota_set(id,
67 48
                                       QUOTAS.get_class_quotas(context, id))
68 49
 
69
-    @wsgi.serializers(xml=QuotaClassTemplate)
70 50
     def update(self, req, id, body):
71 51
         context = req.environ['manila.context']
72 52
         authorize(context)
@@ -89,8 +69,6 @@ class Quota_classes(extensions.ExtensionDescriptor):
89 69
 
90 70
     name = "QuotaClasses"
91 71
     alias = "os-quota-class-sets"
92
-    namespace = ("http://docs.openstack.org/volume/ext/"
93
-                 "quota-classes-sets/api/v1.1")
94 72
     updated = "2012-03-12T00:00:00+00:00"
95 73
 
96 74
     def get_resources(self):

+ 0
- 18
manila/api/contrib/quotas.py View File

@@ -19,8 +19,6 @@ from six.moves.urllib import parse
19 19
 import webob
20 20
 
21 21
 from manila.api import extensions
22
-from manila.api.openstack import wsgi
23
-from manila.api import xmlutil
24 22
 from manila import db
25 23
 from manila.db.sqlalchemy import api as sqlalchemy_api
26 24
 from manila import exception
@@ -38,18 +36,6 @@ authorize_show = extensions.extension_authorizer('compute', 'quotas:show')
38 36
 authorize_delete = extensions.extension_authorizer('compute', 'quotas:delete')
39 37
 
40 38
 
41
-class 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
-
53 39
 class QuotaSetsController(object):
54 40
 
55 41
     def __init__(self, ext_mgr):
@@ -90,7 +76,6 @@ class QuotaSetsController(object):
90 76
         else:
91 77
             return dict((k, v['limit']) for k, v in values.items())
92 78
 
93
-    @wsgi.serializers(xml=QuotaTemplate)
94 79
     def show(self, req, id):
95 80
         context = req.environ['manila.context']
96 81
         authorize_show(context)
@@ -105,7 +90,6 @@ class QuotaSetsController(object):
105 90
         except exception.NotAuthorized:
106 91
             raise webob.exc.HTTPForbidden()
107 92
 
108
-    @wsgi.serializers(xml=QuotaTemplate)
109 93
     def update(self, req, id, body):
110 94
         context = req.environ['manila.context']
111 95
         authorize_update(context)
@@ -209,7 +193,6 @@ class QuotaSetsController(object):
209 193
                 raise webob.exc.HTTPForbidden()
210 194
         return {'quota_set': self._get_quotas(context, id, user_id=user_id)}
211 195
 
212
-    @wsgi.serializers(xml=QuotaTemplate)
213 196
     def defaults(self, req, id):
214 197
         context = req.environ['manila.context']
215 198
         authorize_show(context)
@@ -241,7 +224,6 @@ class Quotas(extensions.ExtensionDescriptor):
241 224
 
242 225
     name = "Quotas"
243 226
     alias = "os-quota-sets"
244
-    namespace = "http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1"
245 227
     updated = "2011-08-08T00:00:00+00:00"
246 228
 
247 229
     def get_resources(self):

+ 0
- 30
manila/api/contrib/services.py View File

@@ -17,8 +17,6 @@ from oslo_log import log
17 17
 import webob.exc
18 18
 
19 19
 from manila.api import extensions
20
-from manila.api.openstack import wsgi
21
-from manila.api import xmlutil
22 20
 from manila import db
23 21
 from manila import exception
24 22
 from manila import utils
@@ -27,33 +25,7 @@ LOG = log.getLogger(__name__)
27 25
 authorize = extensions.extension_authorizer('share', 'services')
28 26
 
29 27
 
30
-class 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
-
44
-class 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
-
55 28
 class ServiceController(object):
56
-    @wsgi.serializers(xml=ServicesIndexTemplate)
57 29
     def index(self, req):
58 30
         """Return a list of all running services."""
59 31
         context = req.environ['manila.context']
@@ -90,7 +62,6 @@ class ServiceController(object):
90 62
 
91 63
         return {'services': services}
92 64
 
93
-    @wsgi.serializers(xml=ServicesUpdateTemplate)
94 65
     def update(self, req, id, body):
95 66
         """Enable/Disable scheduling for a service."""
96 67
         context = req.environ['manila.context']
@@ -126,7 +97,6 @@ class Services(extensions.ExtensionDescriptor):
126 97
 
127 98
     name = "Services"
128 99
     alias = "os-services"
129
-    namespace = "http://docs.openstack.org/volume/ext/services/api/v2"
130 100
     updated = "2012-10-28T00:00:00-00:00"
131 101
 
132 102
     def get_resources(self):

+ 0
- 39
manila/api/contrib/share_actions.py View File

@@ -19,7 +19,6 @@ import webob
19 19
 
20 20
 from manila.api import extensions
21 21
 from manila.api.openstack import wsgi
22
-from manila.api import xmlutil
23 22
 from manila import exception
24 23
 from manila.i18n import _
25 24
 from manila import share
@@ -28,40 +27,6 @@ from manila import share
28 27
 authorize = extensions.extension_authorizer('share', 'services')
29 28
 
30 29
 
31
-class 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
-
50
-class 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
-
65 30
 class ShareActionsController(wsgi.Controller):
66 31
     def __init__(self, *args, **kwargs):
67 32
         super(ShareActionsController, self).__init__(*args, **kwargs)
@@ -118,7 +83,6 @@ class ShareActionsController(wsgi.Controller):
118 83
                 raise webob.exc.HTTPBadRequest(explanation=exc_str)
119 84
 
120 85
     @wsgi.action('os-allow_access')
121
-    @wsgi.serializers(xml=ShareAccessTemplate)
122 86
     def _allow_access(self, req, id, body):
123 87
         """Add share access rule."""
124 88
         context = req.environ['manila.context']
@@ -146,7 +110,6 @@ class ShareActionsController(wsgi.Controller):
146 110
         return {'access': access}
147 111
 
148 112
     @wsgi.action('os-deny_access')
149
-    @wsgi.serializers(xml=ShareAccessTemplate)
150 113
     def _deny_access(self, req, id, body):
151 114
         """Remove access rule."""
152 115
         context = req.environ['manila.context']
@@ -164,7 +127,6 @@ class ShareActionsController(wsgi.Controller):
164 127
         return webob.Response(status_int=202)
165 128
 
166 129
     @wsgi.action('os-access_list')
167
-    @wsgi.serializers(xml=ShareAccessListTemplate)
168 130
     def _access_list(self, req, id, body):
169 131
         """list access rules."""
170 132
         context = req.environ['manila.context']
@@ -183,7 +145,6 @@ class Share_actions(extensions.ExtensionDescriptor):
183 145
 
184 146
     name = 'ShareActions'
185 147
     alias = 'share-actions'
186
-    namespace = ''
187 148
     updated = '2012-08-14T00:00:00+00:00'
188 149
 
189 150
     def get_controller_extensions(self):

+ 0
- 2
manila/api/contrib/share_manage.py View File

@@ -120,8 +120,6 @@ class Share_manage(extensions.ExtensionDescriptor):
120 120
 
121 121
     name = 'ShareManage'
122 122
     alias = 'os-share-manage'
123
-    namespace = ('http://docs.openstack.org/share/ext/'
124
-                 'os-share-manage/api/v1')
125 123
     updated = '2015-02-17T00:00:00+00:00'
126 124
 
127 125
     def get_resources(self):

+ 0
- 49
manila/api/contrib/share_type_access.py View File

@@ -20,7 +20,6 @@ import webob
20 20
 
21 21
 from manila.api import extensions
22 22
 from manila.api.openstack import wsgi
23
-from manila.api import xmlutil
24 23
 from manila import exception
25 24
 from manila.i18n import _
26 25
 from manila.share import share_types
@@ -31,45 +30,6 @@ soft_authorize = extensions.soft_extension_authorizer('share',
31 30
 authorize = extensions.extension_authorizer('share', 'share_type_access')
32 31
 
33 32
 
34
-def make_share_type(elem):
35
-    elem.set('{%s}is_public' % Share_type_access.namespace,
36
-             '%s:is_public' % Share_type_access.alias)
37
-
38
-
39
-def make_share_type_access(elem):
40
-    elem.set('share_type_id')
41
-    elem.set('project_id')
42
-
43
-
44
-class 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
-
53
-class 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
-
64
-class 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
-
73 33
 def _marshall_share_type_access(share_type):
74 34
     rval = []
75 35
     for project_id in share_type['projects']:
@@ -82,7 +42,6 @@ def _marshall_share_type_access(share_type):
82 42
 class ShareTypeAccessController(object):
83 43
     """The share type access API controller for the OpenStack API."""
84 44
 
85
-    @wsgi.serializers(xml=ShareTypeAccessTemplate)
86 45
     def index(self, req, type_id):
87 46
         context = req.environ['manila.context']
88 47
         authorize(context)
@@ -123,8 +82,6 @@ class ShareTypeActionController(wsgi.Controller):
123 82
     def show(self, req, resp_obj, id):
124 83
         context = req.environ['manila.context']
125 84
         if soft_authorize(context):
126
-            # Attach our slave template to the response object
127
-            resp_obj.attach(xml=ShareTypeTemplate())
128 85
             share_type = req.get_db_share_type(id)
129 86
             self._extend_share_type(resp_obj.obj['share_type'], share_type)
130 87
 
@@ -132,8 +89,6 @@ class ShareTypeActionController(wsgi.Controller):
132 89
     def index(self, req, resp_obj):
133 90
         context = req.environ['manila.context']
134 91
         if soft_authorize(context):
135
-            # Attach our slave template to the response object
136
-            resp_obj.attach(xml=ShareTypesTemplate())
137 92
             for share_type_rval in list(resp_obj.obj['share_types']):
138 93
                 type_id = share_type_rval['id']
139 94
                 share_type = req.get_db_share_type(type_id)
@@ -143,8 +98,6 @@ class ShareTypeActionController(wsgi.Controller):
143 98
     def create(self, req, body, resp_obj):
144 99
         context = req.environ['manila.context']
145 100
         if soft_authorize(context):
146
-            # Attach our slave template to the response object
147
-            resp_obj.attach(xml=ShareTypeTemplate())
148 101
             type_id = resp_obj.obj['share_type']['id']
149 102
             share_type = req.get_db_share_type(type_id)
150 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 147
     name = "ShareTypeAccess"
195 148
     alias = "os-share-type-access"
196
-    namespace = ("http://docs.openstack.org/share/"
197
-                 "ext/os-share-type-access/api/v1")
198 149
     updated = "2015-03-02T00:00:00Z"
199 150
 
200 151
     def get_resources(self):

+ 0
- 2
manila/api/contrib/share_unmanage.py View File

@@ -78,8 +78,6 @@ class Share_unmanage(extensions.ExtensionDescriptor):
78 78
 
79 79
     name = 'ShareUnmanage'
80 80
     alias = 'os-share-unmanage'
81
-    namespace = ('http://docs.openstack.org/share/ext/'
82
-                 'os-share-unmanage/api/v1')
83 81
     updated = '2015-02-17T00:00:00+00:00'
84 82
 
85 83
     def get_resources(self):

+ 0
- 1
manila/api/contrib/types_extra_specs.py View File

@@ -162,7 +162,6 @@ class Types_extra_specs(extensions.ExtensionDescriptor):
162 162
 
163 163
     name = "TypesExtraSpecs"
164 164
     alias = "os-types-extra-specs"
165
-    namespace = "http://docs.openstack.org/share/ext/types-extra-specs/api/v1"
166 165
     updated = "2011-08-24T00:00:00+00:00"
167 166
 
168 167
     def get_resources(self):

+ 0
- 1
manila/api/contrib/types_manage.py View File

@@ -124,7 +124,6 @@ class Types_manage(extensions.ExtensionDescriptor):
124 124
 
125 125
     name = "TypesManage"
126 126
     alias = "os-types-manage"
127
-    namespace = "http://docs.openstack.org/share/ext/types-manage/api/v1"
128 127
     updated = "2011-08-24T00:00:00+00:00"
129 128
 
130 129
     def get_controller_extensions(self):

+ 0
- 1
manila/api/contrib/used_limits.py View File

@@ -55,7 +55,6 @@ class Used_limits(extensions.ExtensionDescriptor):
55 55
 
56 56
     name = "UsedLimits"
57 57
     alias = 'os-used-limits'
58
-    namespace = "http://docs.openstack.org/share/ext/used-limits/api/v1.0"
59 58
     updated = "2014-03-27T00:00:00+00:00"
60 59
 
61 60
     def get_controller_extensions(self):

+ 0
- 2
manila/api/contrib/user_quotas.py View File

@@ -22,6 +22,4 @@ class User_quotas(extensions.ExtensionDescriptor):
22 22
 
23 23
     name = "UserQuotas"
24 24
     alias = "os-user-quotas"
25
-    namespace = ("http://docs.openstack.org/compute/ext/user_quotas"
26
-                 "/api/v1.1")
27 25
     updated = "2013-07-18T00:00:00+00:00"

+ 0
- 58
manila/api/extensions.py View File

@@ -25,7 +25,6 @@ import webob.exc
25 25
 
26 26
 import manila.api.openstack
27 27
 from manila.api.openstack import wsgi
28
-from manila.api import xmlutil
29 28
 from manila import exception
30 29
 from manila.i18n import _LE
31 30
 from manila.i18n import _LI
@@ -52,10 +51,6 @@ class ExtensionDescriptor(object):
52 51
 
53 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 54
     # The timestamp when the extension was last updated, e.g.,
60 55
     # '2011-01-22T13:25:27-06:00'
61 56
     updated = None
@@ -83,55 +78,6 @@ class ExtensionDescriptor(object):
83 78
         controller_exts = []
84 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
-
105
-def 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
-
117
-ext_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
118
-
119
-
120
-class 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
-
127
-class 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
 
136 82
 class ExtensionsResource(wsgi.Resource):
137 83
 
@@ -144,19 +90,16 @@ class ExtensionsResource(wsgi.Resource):
144 90
         ext_data['name'] = ext.name
145 91
         ext_data['alias'] = ext.alias
146 92
         ext_data['description'] = ext.__doc__
147
-        ext_data['namespace'] = ext.namespace
148 93
         ext_data['updated'] = ext.updated
149 94
         ext_data['links'] = []  # TODO(dprince): implement extension links
150 95
         return ext_data
151 96
 
152
-    @wsgi.serializers(xml=ExtensionsTemplate)
153 97
     def index(self, req):
154 98
         extensions = []
155 99
         for _alias, ext in six.iteritems(self.extension_manager.extensions):
156 100
             extensions.append(self._translate(ext))
157 101
         return dict(extensions=extensions)
158 102
 
159
-    @wsgi.serializers(xml=ExtensionTemplate)
160 103
     def show(self, req, id):
161 104
         try:
162 105
             # NOTE(dprince): the extensions alias is used as the 'id' for show
@@ -240,7 +183,6 @@ class ExtensionManager(object):
240 183
             LOG.debug('Ext alias: %s', extension.alias)
241 184
             LOG.debug('Ext description: %s',
242 185
                       ' '.join(extension.__doc__.strip().split()))
243
-            LOG.debug('Ext namespace: %s', extension.namespace)
244 186
             LOG.debug('Ext updated: %s', extension.updated)
245 187
         except AttributeError as ex:
246 188
             LOG.exception(_LE("Exception loading extension: %s"),

+ 3
- 236
manila/api/openstack/wsgi.py View File

@@ -28,37 +28,14 @@ from manila.i18n import _LI
28 28
 from manila import utils
29 29
 from manila import wsgi
30 30
 
31
-from lxml import etree
32
-from xml.dom import minidom
33
-from xml.parsers import expat
34
-
35
-
36
-XMLNS_V1 = 'http://docs.openstack.org/volume/api/v1'
37
-XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
38
-
39 31
 LOG = 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
-
49 33
 SUPPORTED_CONTENT_TYPES = (
50 34
     'application/json',
51
-    'application/vnd.openstack.volume+json',
52
-    'application/xml',
53
-    'application/vnd.openstack.volume+xml',
54 35
 )
55 36
 
56 37
 _MEDIA_TYPE_MAP = {
57
-    'application/vnd.openstack.volume+json': 'json',
58 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 229
         return {'body': self._from_json(datastring)}
253 230
 
254 231
 
255
-class 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
-
332
-class 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
-
344 232
 class DictSerializer(ActionDispatcher):
345 233
     """Default request body serialization"""
346 234
 
@@ -358,110 +246,6 @@ class JSONDictSerializer(DictSerializer):
358 246
         return jsonutils.dumps(data)
359 247
 
360 248
 
361
-class 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
-
465 249
 def serializers(**serializers):
466 250
     """Attaches serializers to a method.
467 251
 
@@ -660,15 +444,6 @@ def action_peek_json(body):
660 444
     return decoded.keys()[0]
661 445
 
662 446
 
663
-def 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
-
672 447
 class ResourceExceptionHandler(object):
673 448
     """Context manager to handle Resource exceptions.
674 449
 
@@ -731,16 +506,13 @@ class Resource(wsgi.Application):
731 506
 
732 507
         self.controller = controller
733 508
 
734
-        default_deserializers = dict(xml=XMLDeserializer,
735
-                                     json=JSONDeserializer)
509
+        default_deserializers = dict(json=JSONDeserializer)
736 510
         default_deserializers.update(deserializers)
737 511
 
738 512
         self.default_deserializers = default_deserializers
739
-        self.default_serializers = dict(xml=XMLDictSerializer,
740
-                                        json=JSONDictSerializer)
513
+        self.default_serializers = dict(json=JSONDictSerializer)
741 514
 
742
-        self.action_peek = dict(xml=action_peek_xml,
743
-                                json=action_peek_json)
515
+        self.action_peek = dict(json=action_peek_json)
744 516
         self.action_peek.update(action_peek or {})
745 517
 
746 518
         # Copy over the actions dictionary
@@ -1186,11 +958,8 @@ class Fault(webob.exc.HTTPException):
1186 958
         # 'code' is an attribute on the fault tag itself
1187 959
         metadata = {'attributes': {fault_name: 'code'}}
1188 960
 
1189
-        xml_serializer = XMLDictSerializer(metadata, XMLNS_V1)
1190
-
1191 961
         content_type = req.best_match_content_type()
1192 962
         serializer = {
1193
-            'application/xml': xml_serializer,
1194 963
             'application/json': JSONDictSerializer(),
1195 964
         }[content_type]
1196 965
 
@@ -1245,9 +1014,7 @@ class OverLimitFault(webob.exc.HTTPException):
1245 1014
         content_type = request.best_match_content_type()
1246 1015
         metadata = {"attributes": {"overLimitFault": "code"}}
1247 1016
 
1248
-        xml_serializer = XMLDictSerializer(metadata, XMLNS_V1)
1249 1017
         serializer = {
1250
-            'application/xml': xml_serializer,
1251 1018
             'application/json': JSONDictSerializer(),
1252 1019
         }[content_type]
1253 1020
 

+ 0
- 141
manila/api/schemas/atom-link.rng View File

@@ -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>

+ 0
- 11
manila/api/schemas/v1.1/extension.rng View File

@@ -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>

+ 0
- 6
manila/api/schemas/v1.1/extensions.rng View File

@@ -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>

+ 0
- 28
manila/api/schemas/v1.1/limits.rng View File

@@ -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>

+ 0
- 9
manila/api/schemas/v1.1/metadata.rng View File

@@ -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>

+ 1
- 9
manila/api/urlmap.py View File

@@ -253,24 +253,16 @@ class URLMap(paste.urlmap.URLMap):
253 253
         path_info = environ['PATH_INFO']
254 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 256
         # The API version is determined in one of three ways:
261 257
         # 1) URL path prefix (eg /v1.1/tenant/servers/detail)
262 258
         # 2) Content-Type header (eg application/json;version=1.1)
263 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 262
         supported_content_types = list(wsgi.SUPPORTED_CONTENT_TYPES)
266 263
 
267 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 266
         if not app:
275 267
             app = self._content_type_strategy(host, port, environ)
276 268
 

+ 0
- 30
manila/api/v1/limits.py View File

@@ -31,7 +31,6 @@ import webob.exc
31 31
 
32 32
 from manila.api.openstack import wsgi
33 33
 from manila.api.views import limits as limits_views
34
-from manila.api import xmlutil
35 34
 from manila.i18n import _
36 35
 from manila import quota
37 36
 from manila import wsgi as base_wsgi
@@ -46,38 +45,9 @@ PER_HOUR = 60 * 60
46 45
 PER_DAY = 60 * 60 * 24
47 46
 
48 47
 
49
-limits_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
50
-
51
-
52
-class 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
-
77 48
 class LimitsController(object):
78 49
     """Controller for accessing limits in the OpenStack API."""
79 50
 
80
-    @wsgi.serializers(xml=LimitsTemplate)
81 51
     def index(self, req):
82 52
         """Return all global and rate limit information."""
83 53
         context = req.environ['manila.context']

+ 0
- 30
manila/api/v1/security_service.py View File

@@ -23,7 +23,6 @@ from webob import exc
23 23
 from manila.api import common
24 24
 from manila.api.openstack import wsgi
25 25
 from manila.api.views import security_service as security_service_views
26
-from manila.api import xmlutil
27 26
 from manila.common import constants
28 27
 from manila import db
29 28
 from manila import exception
@@ -36,36 +35,11 @@ RESOURCE_NAME = 'security_service'
36 35
 LOG = log.getLogger(__name__)
37 36
 
38 37
 
39
-def 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
-
46
-class 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
-
54
-class 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
-
63 38
 class SecurityServiceController(wsgi.Controller):
64 39
     """The Shares API controller for the OpenStack API."""
65 40
 
66 41
     _view_builder_class = security_service_views.ViewBuilder
67 42
 
68
-    @wsgi.serializers(xml=SecurityServiceTemplate)
69 43
     def show(self, req, id):
70 44
         """Return data about the given security service."""
71 45
         context = req.environ['manila.context']
@@ -102,14 +76,12 @@ class SecurityServiceController(wsgi.Controller):
102 76
 
103 77
         return webob.Response(status_int=202)
104 78
 
105
-    @wsgi.serializers(xml=SecurityServicesTemplate)
106 79
     def index(self, req):
107 80
         """Returns a summary list of security services."""
108 81
         policy.check_policy(req.environ['manila.context'], RESOURCE_NAME,
109 82
                             'index')
110 83
         return self._get_security_services(req, is_detail=False)
111 84
 
112
-    @wsgi.serializers(xml=SecurityServicesTemplate)
113 85
     def detail(self, req):
114 86
         """Returns a detailed list of security services."""
115 87
         policy.check_policy(req.environ['manila.context'], RESOURCE_NAME,
@@ -181,7 +153,6 @@ class SecurityServiceController(wsgi.Controller):
181 153
                 return True
182 154
         return False
183 155
 
184
-    @wsgi.serializers(xml=SecurityServiceTemplate)
185 156
     def update(self, req, id, body):
186 157
         """Update a security service."""
187 158
         context = req.environ['manila.context']
@@ -221,7 +192,6 @@ class SecurityServiceController(wsgi.Controller):
221 192
         security_service = db.security_service_update(context, id, update_dict)
222 193
         return self._view_builder.detail(req, security_service)
223 194
 
224
-    @wsgi.serializers(xml=SecurityServiceTemplate)
225 195
     def create(self, req, body):
226 196
         """Creates a new security service."""
227 197
         context = req.environ['manila.context']

+ 0
- 9
manila/api/v1/share_metadata.py View File

@@ -16,7 +16,6 @@
16 16
 import webob
17 17
 from webob import exc
18 18
 
19
-from manila.api import common
20 19
 from manila.api.openstack import wsgi
21 20
 from manila import exception
22 21
 from manila.i18n import _
@@ -39,14 +38,11 @@ class ShareMetadataController(object):
39 38
             raise exc.HTTPNotFound(explanation=msg)
40 39
         return meta
41 40
 
42
-    @wsgi.serializers(xml=common.MetadataTemplate)
43 41
     def index(self, req, share_id):
44 42
         """Returns the list of metadata for a given share."""
45 43
         context = req.environ['manila.context']
46 44
         return {'metadata': self._get_metadata(context, share_id)}
47 45
 
48
-    @wsgi.serializers(xml=common.MetadataTemplate)
49
-    @wsgi.deserializers(xml=common.MetadataDeserializer)
50 46
     def create(self, req, share_id, body):
51 47
         try:
52 48
             metadata = body['metadata']
@@ -63,8 +59,6 @@ class ShareMetadataController(object):
63 59
 
64 60
         return {'metadata': new_metadata}
65 61
 
66
-    @wsgi.serializers(xml=common.MetaItemTemplate)
67
-    @wsgi.deserializers(xml=common.MetaItemDeserializer)
68 62
     def update(self, req, share_id, id, body):
69 63
         try:
70 64
             meta_item = body['meta']
@@ -88,8 +82,6 @@ class ShareMetadataController(object):
88 82
 
89 83
         return {'meta': meta_item}
90 84
 
91
-    @wsgi.serializers(xml=common.MetadataTemplate)
92
-    @wsgi.deserializers(xml=common.MetadataDeserializer)
93 85
     def update_all(self, req, share_id, body):
94 86
         try:
95 87
             metadata = body['metadata']
@@ -125,7 +117,6 @@ class ShareMetadataController(object):
125 117
         except exception.InvalidShareMetadataSize as error:
126 118
             raise exc.HTTPBadRequest(explanation=error.msg)
127 119
 
128
-    @wsgi.serializers(xml=common.MetaItemTemplate)
129 120
     def show(self, req, share_id, id):
130 121
         """Return a single metadata item."""
131 122
         context = req.environ['manila.context']

+ 0
- 45
manila/api/v1/share_networks.py View File

@@ -25,7 +25,6 @@ from webob import exc
25 25
 from manila.api import common
26 26
 from manila.api.openstack import wsgi
27 27
 from manila.api.views import share_networks as share_networks_views
28
-from manila.api import xmlutil
29 28
 from manila.db import api as db_api
30 29
 from manila import exception
31 30
 from manila.i18n import _
@@ -39,43 +38,6 @@ RESOURCE_NAME = 'share_network'
39 38
 RESOURCES_NAME = 'share_networks'
40 39
 LOG = log.getLogger(__name__)
41 40
 QUOTAS = quota.QUOTAS
42
-SHARE_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
-
60
-def _make_share_network(elem):
61
-    for attr in SHARE_NETWORK_ATTRS:
62
-        elem.set(attr)
63
-
64
-
65
-class 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
-
72
-class 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
 
81 43
 class ShareNetworkController(wsgi.Controller):
@@ -87,7 +49,6 @@ class ShareNetworkController(wsgi.Controller):
87 49
         super(ShareNetworkController, self).__init__()
88 50
         self.share_rpcapi = share_rpcapi.ShareAPI()
89 51
 
90
-    @wsgi.serializers(xml=ShareNetworkTemplate)
91 52
     def show(self, req, id):
92 53
         """Return data about the requested network info."""
93 54
         context = req.environ['manila.context']
@@ -132,7 +93,6 @@ class ShareNetworkController(wsgi.Controller):
132 93
                           project_id=share_network['project_id'])
133 94
         return webob.Response(status_int=202)
134 95
 
135
-    @wsgi.serializers(xml=ShareNetworksTemplate)
136 96
     def _get_share_networks(self, req, is_detail=True):
137 97
         """Returns a list of share networks."""
138 98
         context = req.environ['manila.context']
@@ -200,14 +160,12 @@ class ShareNetworkController(wsgi.Controller):
200 160
         limited_list = common.limited(networks, req)
201 161
         return self._view_builder.build_share_networks(limited_list, is_detail)
202 162
 
203
-    @wsgi.serializers(xml=ShareNetworksTemplate)
204 163
     def index(self, req):
205 164
         """Returns a summary list of share networks."""
206 165
         policy.check_policy(req.environ['manila.context'], RESOURCE_NAME,
207 166
                             'index')
208 167
         return self._get_share_networks(req, is_detail=False)
209 168
 
210
-    @wsgi.serializers(xml=ShareNetworksTemplate)
211 169
     def detail(self, req):
212 170
         """Returns a detailed list of share networks."""
213 171
         policy.check_policy(req.environ['manila.context'], RESOURCE_NAME,
@@ -231,7 +189,6 @@ class ShareNetworkController(wsgi.Controller):
231 189
                     "exclusive. Only one of these are allowed at a time.")
232 190
             raise exc.HTTPBadRequest(explanation=msg)
233 191
 
234
-    @wsgi.serializers(xml=ShareNetworkTemplate)
235 192
     def update(self, req, id, body):
236 193
         """Update specified share network."""
237 194
         context = req.environ['manila.context']
@@ -267,7 +224,6 @@ class ShareNetworkController(wsgi.Controller):
267 224
 
268 225
         return self._view_builder.build_share_network(share_network)
269 226
 
270
-    @wsgi.serializers(xml=ShareNetworkTemplate)
271 227
     def create(self, req, body):
272 228
         """Creates a new share network."""
273 229
         context = req.environ['manila.context']
@@ -309,7 +265,6 @@ class ShareNetworkController(wsgi.Controller):
309 265
             QUOTAS.commit(context, reservations)
310 266
             return self._view_builder.build_share_network(share_network)
311 267
 
312
-    @wsgi.serializers(xml=ShareNetworkTemplate)
313 268
     def action(self, req, id, body):
314 269
         _actions = {
315 270
             'add_security_service': self._add_security_service,

+ 0
- 34
manila/api/v1/share_servers.py View File

@@ -20,7 +20,6 @@ from webob import exc
20 20
 
21 21
 from manila.api.openstack import wsgi
22 22
 from manila.api.views import share_servers as share_servers_views
23
-from manila.api import xmlutil
24 23
 from manila.common import constants
25 24
 from manila.db import api as db_api
26 25
 from manila import exception
@@ -31,37 +30,6 @@ from manila import share
31 30
 RESOURCE_NAME = 'share_server'
32 31
 RESOURCES_NAME = 'share_servers'
33 32
 LOG = log.getLogger(__name__)
34
-SHARE_SERVER_ATTRS = (
35
-    'id',
36
-    'project_id',
37
-    'updated_at',
38
-    'status',
39
-    'host',
40
-    'share_network_name',
41
-)
42
-
43
-
44
-def _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
-
51
-class 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
-
58
-class 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
 
67 35
 class ShareServerController(wsgi.Controller):
@@ -72,7 +40,6 @@ class ShareServerController(wsgi.Controller):
72 40
         self._view_builder_class = share_servers_views.ViewBuilder
73 41
         super(ShareServerController, self).__init__()
74 42
 
75
-    @wsgi.serializers(xml=ShareServersTemplate)
76 43
     def index(self, req):
77 44
         """Returns a list of share servers."""
78 45
 
@@ -98,7 +65,6 @@ class ShareServerController(wsgi.Controller):
98 65
                                         s.share_network['id']])]
99 66
         return self._view_builder.build_share_servers(share_servers)
100 67
 
101
-    @wsgi.serializers(xml=ShareServerTemplate)
102 68
     def show(self, req, id):
103 69
         """Return data about the requested share server."""
104 70
         context = req.environ['manila.context']

+ 0
- 45
manila/api/v1/share_snapshots.py View File

@@ -23,7 +23,6 @@ from webob import exc
23 23
 from manila.api import common
24 24
 from manila.api.openstack import wsgi
25 25
 from manila.api.views import share_snapshots as snapshot_views
26
-from manila.api import xmlutil
27 26
 from manila import exception
28 27
 from manila.i18n import _LI
29 28
 from manila import share
@@ -31,30 +30,6 @@ from manila import share
31 30
 LOG = log.getLogger(__name__)
32 31
 
33 32
 
34
-def 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
-
41
-class 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
-
49
-class 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
-
58 33
 class ShareSnapshotsController(wsgi.Controller):
59 34
     """The Share Snapshots API controller for the OpenStack API."""
60 35
 
@@ -64,7 +39,6 @@ class ShareSnapshotsController(wsgi.Controller):
64 39
         super(ShareSnapshotsController, self).__init__()
65 40
         self.share_api = share.API()
66 41
 
67
-    @wsgi.serializers(xml=SnapshotTemplate)
68 42
     def show(self, req, id):
69 43
         """Return data about the given snapshot."""
70 44
         context = req.environ['manila.context']
@@ -89,12 +63,10 @@ class ShareSnapshotsController(wsgi.Controller):
89 63
             raise exc.HTTPNotFound()
90 64
         return webob.Response(status_int=202)
91 65
 
92
-    @wsgi.serializers(xml=SnapshotsTemplate)
93 66
     def index(self, req):
94 67
         """Returns a summary list of snapshots."""
95 68
         return self._get_snapshots(req, is_detail=False)
96 69
 
97
-    @wsgi.serializers(xml=SnapshotsTemplate)
98 70
     def detail(self, req):
99 71
         """Returns a detailed list of snapshots."""
100 72
         return self._get_snapshots(req, is_detail=True)
@@ -138,7 +110,6 @@ class ShareSnapshotsController(wsgi.Controller):
138 110
         """Return share search options allowed by non-admin."""
139 111
         return ('display_name', 'name', 'status', 'share_id', 'size')
140 112
 
141
-    @wsgi.serializers(xml=SnapshotTemplate)
142 113
     def update(self, req, id, body):
143 114
         """Update a snapshot."""
144 115
         context = req.environ['manila.context']
@@ -167,7 +138,6 @@ class ShareSnapshotsController(wsgi.Controller):
167 138
         return self._view_builder.detail(req, snapshot)
168 139
 
169 140
     @wsgi.response(202)
170
-    @wsgi.serializers(xml=SnapshotTemplate)
171 141
     def create(self, req, body):
172 142
         """Creates a new snapshot."""
173 143
         context = req.environ['manila.context']
@@ -204,18 +174,3 @@ class ShareSnapshotsController(wsgi.Controller):
204 174
 
205 175
 def create_resource():
206 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]

+ 0
- 32
manila/api/v1/shares.py View File

@@ -26,7 +26,6 @@ from webob import exc
26 26
 from manila.api import common
27 27
 from manila.api.openstack import wsgi
28 28
 from manila.api.views import shares as share_views
29
-from manila.api import xmlutil
30 29
 from manila import exception
31 30
 from manila.i18n import _
32 31
 from manila.i18n import _LI
@@ -36,32 +35,6 @@ from manila.share import share_types
36 35
 LOG = log.getLogger(__name__)
37 36
 
38 37
 
39
-def 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
-
50
-class 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
-
57
-class 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
-
65 38
 class ShareController(wsgi.Controller):
66 39
     """The Shares API controller for the OpenStack API."""
67 40
 
@@ -71,7 +44,6 @@ class ShareController(wsgi.Controller):
71 44
         super(ShareController, self).__init__()
72 45
         self.share_api = share.API()
73 46
 
74
-    @wsgi.serializers(xml=ShareTemplate)
75 47
     def show(self, req, id):
76 48
         """Return data about the given share."""
77 49
         context = req.environ['manila.context']
@@ -99,12 +71,10 @@ class ShareController(wsgi.Controller):
99 71
 
100 72
         return webob.Response(status_int=202)
101 73
 
102
-    @wsgi.serializers(xml=SharesTemplate)
103 74
     def index(self, req):
104 75
         """Returns a summary list of shares."""
105 76
         return self._get_shares(req, is_detail=False)
106 77
 
107
-    @wsgi.serializers(xml=SharesTemplate)
108 78
     def detail(self, req):
109 79
         """Returns a detailed list of shares."""
110 80
         return self._get_shares(req, is_detail=True)
@@ -164,7 +134,6 @@ class ShareController(wsgi.Controller):
164 134
             'is_public', 'metadata', 'extra_specs', 'sort_key', 'sort_dir',
165 135
         )
166 136
 
167
-    @wsgi.serializers(xml=ShareTemplate)
168 137
     def update(self, req, id, body):
169 138
         """Update a share."""
170 139
         context = req.environ['manila.context']
@@ -192,7 +161,6 @@ class ShareController(wsgi.Controller):
192 161
         share.update(update_dict)
193 162
         return self._view_builder.detail(req, share)
194 163
 
195
-    @wsgi.serializers(xml=ShareTemplate)
196 164
     def create(self, req, body):
197 165
         """Creates a new share."""
198 166
         context = req.environ['manila.context']

+ 0
- 157
manila/api/versions.py View File

@@ -13,14 +13,10 @@
13 13
 #    License for the specific language governing permissions and limitations
14 14
 #    under the License.
15 15
 
16
-import datetime
17
-
18
-from lxml import etree
19 16
 from oslo_config import cfg
20 17
 
21 18
 from manila.api.openstack import wsgi
22 19
 from manila.api.views import versions as views_versions
23
-from manila.api import xmlutil
24 20
 
25 21
 CONF = cfg.CONF
26 22
 
@@ -46,13 +42,8 @@ _KNOWN_VERSIONS = {
46 42
             },
47 43
         ],
48 44
         "media-types": [
49
-            {
50
-                "base": "application/xml",
51
-                "type": "application/vnd.openstack.volume+xml;version=1",
52
-            },
53 45
             {
54 46
                 "base": "application/json",
55
-                "type": "application/vnd.openstack.volume+json;version=1",
56 47
             }
57 48
         ],
58 49
     },
@@ -76,13 +67,8 @@ _KNOWN_VERSIONS = {
76 67
             },
77 68
         ],
78 69
         "media-types": [
79
-            {
80
-                "base": "application/xml",
81
-                "type": "application/vnd.openstack.volume+xml;version=1",
82
-            },
83 70
             {
84 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 87
     return versions
102 88
 
103 89
 
104
-class MediaTypesTemplateElement(xmlutil.TemplateElement):
105
-    def will_render(self, datum):
106
-        return 'media-types' in datum
107
-
108
-
109
-def 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
-
124
-version_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
125
-
126
-
127
-class 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
-
134
-class 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
-
142
-class 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
-
150
-class 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
-
226
-class 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
-
234
-class 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
-
242 90
 class Versions(wsgi.Resource):
243 91
 
244 92
     def __init__(self):
245 93
         super(Versions, self).__init__(None)
246 94
 
247
-    @wsgi.serializers(xml=VersionsTemplate,
248
-                      atom=VersionsAtomSerializer)
249 95
     def index(self, req):
250 96
         """Return all versions."""
251 97
         builder = views_versions.get_view_builder(req)
252 98
         return builder.build_versions(get_supported_versions())
253 99
 
254
-    @wsgi.serializers(xml=ChoicesTemplate)
255 100
     @wsgi.response(300)
256 101
     def multi(self, req):
257 102
         """Return multiple choices."""
@@ -270,8 +115,6 @@ class Versions(wsgi.Resource):
270 115
 
271 116
 
272 117
 class ShareVersionV1(object):
273
-    @wsgi.serializers(xml=VersionTemplate,
274
-                      atom=VersionAtomSerializer)
275 118
     def show(self, req):
276 119
         builder = views_versions.get_view_builder(req)
277 120
         return builder.build_version(_KNOWN_VERSIONS['v1.0'])

+ 0
- 913
manila/api/xmlutil.py View File

@@ -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
-
16
-import os.path
17
-
18
-from lxml import etree
19
-import six
20
-
21
-from manila.i18n import _
22
-from manila import utils
23
-
24
-
25
-XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
26
-XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
27
-XMLNS_COMMON_V10 = 'http://docs.openstack.org/common/api/v1.0'
28
-XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
29
-XMLNS_VOLUME_V1 = 'http://docs.openstack.org/volume/api/v1'
30
-XMLNS_VOLUME_V2 = ('http://docs.openstack.org/api/openstack-volume/2.0/'
31
-                   'content')
32
-XMLNS_SHARE_V1 = ''
33
-
34
-
35
-def 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
-
48
-class 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
-
95
-def get_items(obj):
96
-    """Get items in obj."""
97
-
98
-    return list(obj.items())
99
-
100
-
101
-class 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
-
112
-class 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
-
138
-class 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
-
496
-def 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
-
519
-class 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.