Nuke XML support from Nova REST API - Phase 2
In I5a580fc323c3809790b4a68a9f8f8129ecdc2cf0 we switched off XML support. In this review we entirely remove all support for XML in the API. Change-Id: Id384d0e8d350fdd68ed03c83b94f6e558d53eb28
This commit is contained in:
parent
3ae2ddd4a2
commit
8454a2b4f3
|
@ -23,8 +23,6 @@ import six.moves.urllib.parse as urlparse
|
||||||
import webob
|
import webob
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.compute import task_states
|
from nova.compute import task_states
|
||||||
from nova.compute import utils as compute_utils
|
from nova.compute import utils as compute_utils
|
||||||
from nova.compute import vm_states
|
from nova.compute import vm_states
|
||||||
|
@ -419,78 +417,6 @@ def raise_http_conflict_for_instance_invalid_state(exc, action, server_id):
|
||||||
raise webob.exc.HTTPConflict(explanation=msg)
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
|
||||||
|
|
||||||
class MetadataDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
def deserialize(self, text):
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(text)
|
|
||||||
metadata_node = self.find_first_child_named(dom, "metadata")
|
|
||||||
metadata = self.extract_metadata(metadata_node)
|
|
||||||
return {'body': {'metadata': metadata}}
|
|
||||||
|
|
||||||
|
|
||||||
class MetaItemDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
def deserialize(self, text):
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(text)
|
|
||||||
metadata_item = self.extract_metadata(dom)
|
|
||||||
return {'body': {'meta': metadata_item}}
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataXMLDeserializer(wsgi.XMLDeserializer):
|
|
||||||
|
|
||||||
def extract_metadata(self, metadata_node):
|
|
||||||
"""Marshal the metadata attribute of a parsed request."""
|
|
||||||
if metadata_node is None:
|
|
||||||
return {}
|
|
||||||
metadata = {}
|
|
||||||
for meta_node in self.find_children_named(metadata_node, "meta"):
|
|
||||||
key = meta_node.getAttribute("key")
|
|
||||||
metadata[key] = self.extract_text(meta_node)
|
|
||||||
return metadata
|
|
||||||
|
|
||||||
def _extract_metadata_container(self, datastring):
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(datastring)
|
|
||||||
metadata_node = self.find_first_child_named(dom, "metadata")
|
|
||||||
metadata = self.extract_metadata(metadata_node)
|
|
||||||
return {'body': {'metadata': metadata}}
|
|
||||||
|
|
||||||
def create(self, datastring):
|
|
||||||
return self._extract_metadata_container(datastring)
|
|
||||||
|
|
||||||
def update_all(self, datastring):
|
|
||||||
return self._extract_metadata_container(datastring)
|
|
||||||
|
|
||||||
def update(self, datastring):
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(datastring)
|
|
||||||
metadata_item = self.extract_metadata(dom)
|
|
||||||
return {'body': {'meta': metadata_item}}
|
|
||||||
|
|
||||||
|
|
||||||
metadata_nsmap = {None: xmlutil.XMLNS_V11}
|
|
||||||
|
|
||||||
|
|
||||||
class MetaItemTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
sel = xmlutil.Selector('meta', xmlutil.get_items, 0)
|
|
||||||
root = xmlutil.TemplateElement('meta', selector=sel)
|
|
||||||
root.set('key', 0)
|
|
||||||
root.text = 1
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=metadata_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataTemplateElement(xmlutil.TemplateElement):
|
|
||||||
def will_render(self, datum):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = MetadataTemplateElement('metadata', selector='metadata')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'meta',
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
elem.set('key', 0)
|
|
||||||
elem.text = 1
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=metadata_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
def check_snapshots_enabled(f):
|
def check_snapshots_enabled(f):
|
||||||
@functools.wraps(f)
|
@functools.wraps(f)
|
||||||
def inner(*args, **kwargs):
|
def inner(*args, **kwargs):
|
||||||
|
|
|
@ -17,7 +17,6 @@ import webob
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.console import api as console_api
|
from nova.console import api as console_api
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
|
||||||
|
@ -44,47 +43,12 @@ def _translate_detail_keys(cons):
|
||||||
return dict(console=info)
|
return dict(console=info)
|
||||||
|
|
||||||
|
|
||||||
class ConsoleTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('console', selector='console')
|
|
||||||
|
|
||||||
id_elem = xmlutil.SubTemplateElement(root, 'id', selector='id')
|
|
||||||
id_elem.text = xmlutil.Selector()
|
|
||||||
|
|
||||||
port_elem = xmlutil.SubTemplateElement(root, 'port', selector='port')
|
|
||||||
port_elem.text = xmlutil.Selector()
|
|
||||||
|
|
||||||
host_elem = xmlutil.SubTemplateElement(root, 'host', selector='host')
|
|
||||||
host_elem.text = xmlutil.Selector()
|
|
||||||
|
|
||||||
passwd_elem = xmlutil.SubTemplateElement(root, 'password',
|
|
||||||
selector='password')
|
|
||||||
passwd_elem.text = xmlutil.Selector()
|
|
||||||
|
|
||||||
constype_elem = xmlutil.SubTemplateElement(root, 'console_type',
|
|
||||||
selector='console_type')
|
|
||||||
constype_elem.text = xmlutil.Selector()
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ConsolesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('consoles')
|
|
||||||
console = xmlutil.SubTemplateElement(root, 'console',
|
|
||||||
selector='consoles')
|
|
||||||
console.append(ConsoleTemplate())
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(object):
|
class Controller(object):
|
||||||
"""The Consoles controller for the OpenStack API."""
|
"""The Consoles controller for the OpenStack API."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.console_api = console_api.API()
|
self.console_api = console_api.API()
|
||||||
|
|
||||||
@wsgi.serializers(xml=ConsolesTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
"""Returns a list of consoles for this instance."""
|
"""Returns a list of consoles for this instance."""
|
||||||
consoles = self.console_api.get_consoles(
|
consoles = self.console_api.get_consoles(
|
||||||
|
@ -102,7 +66,6 @@ class Controller(object):
|
||||||
except exception.InstanceNotFound as e:
|
except exception.InstanceNotFound as e:
|
||||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||||
|
|
||||||
@wsgi.serializers(xml=ConsoleTemplate)
|
|
||||||
def show(self, req, server_id, id):
|
def show(self, req, server_id, id):
|
||||||
"""Shows in-depth information on a specific console."""
|
"""Shows in-depth information on a specific console."""
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import objects
|
from nova import objects
|
||||||
|
@ -27,21 +25,6 @@ from nova import utils
|
||||||
authorize = extensions.extension_authorizer('compute', 'agents')
|
authorize = extensions.extension_authorizer('compute', 'agents')
|
||||||
|
|
||||||
|
|
||||||
class AgentsIndexTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('agents')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'agent', selector='agents')
|
|
||||||
elem.set('hypervisor')
|
|
||||||
elem.set('os')
|
|
||||||
elem.set('architecture')
|
|
||||||
elem.set('version')
|
|
||||||
elem.set('md5hash')
|
|
||||||
elem.set('agent_id')
|
|
||||||
elem.set('url')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class AgentController(object):
|
class AgentController(object):
|
||||||
"""The agent is talking about guest agent.The host can use this for
|
"""The agent is talking about guest agent.The host can use this for
|
||||||
things like accessing files on the disk, configuring networking,
|
things like accessing files on the disk, configuring networking,
|
||||||
|
@ -64,7 +47,6 @@ class AgentController(object):
|
||||||
http://wiki.openstack.org/GuestAgent
|
http://wiki.openstack.org/GuestAgent
|
||||||
http://wiki.openstack.org/GuestAgentXenStoreCommunication
|
http://wiki.openstack.org/GuestAgentXenStoreCommunication
|
||||||
"""
|
"""
|
||||||
@wsgi.serializers(xml=AgentsIndexTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return a list of all agent builds. Filter by hypervisor."""
|
"""Return a list of all agent builds. Filter by hypervisor."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -18,7 +18,6 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -30,25 +29,12 @@ authorize = extensions.extension_authorizer('compute',
|
||||||
'os-assisted-volume-snapshots')
|
'os-assisted-volume-snapshots')
|
||||||
|
|
||||||
|
|
||||||
def make_snapshot(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('volumeId')
|
|
||||||
|
|
||||||
|
|
||||||
class SnapshotTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('snapshot', selector='snapshot')
|
|
||||||
make_snapshot(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class AssistedVolumeSnapshotsController(wsgi.Controller):
|
class AssistedVolumeSnapshotsController(wsgi.Controller):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.compute_api = compute.API()
|
self.compute_api = compute.API()
|
||||||
super(AssistedVolumeSnapshotsController, self).__init__()
|
super(AssistedVolumeSnapshotsController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=SnapshotTemplate)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Creates a new snapshot."""
|
"""Creates a new snapshot."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -14,10 +14,8 @@
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova.api.openstack import common
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import availability_zones
|
from nova import availability_zones
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova import servicegroup
|
from nova import servicegroup
|
||||||
|
@ -30,43 +28,6 @@ authorize_detail = extensions.extension_authorizer('compute',
|
||||||
'availability_zone:detail')
|
'availability_zone:detail')
|
||||||
|
|
||||||
|
|
||||||
def make_availability_zone(elem):
|
|
||||||
elem.set('name', 'zoneName')
|
|
||||||
|
|
||||||
zoneStateElem = xmlutil.SubTemplateElement(elem, 'zoneState',
|
|
||||||
selector='zoneState')
|
|
||||||
zoneStateElem.set('available')
|
|
||||||
|
|
||||||
hostsElem = xmlutil.SubTemplateElement(elem, 'hosts', selector='hosts')
|
|
||||||
hostElem = xmlutil.SubTemplateElement(hostsElem, 'host',
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
hostElem.set('name', 0)
|
|
||||||
|
|
||||||
svcsElem = xmlutil.SubTemplateElement(hostElem, 'services', selector=1)
|
|
||||||
svcElem = xmlutil.SubTemplateElement(svcsElem, 'service',
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
svcElem.set('name', 0)
|
|
||||||
|
|
||||||
svcStateElem = xmlutil.SubTemplateElement(svcElem, 'serviceState',
|
|
||||||
selector=1)
|
|
||||||
svcStateElem.set('available')
|
|
||||||
svcStateElem.set('active')
|
|
||||||
svcStateElem.set('updated_at')
|
|
||||||
|
|
||||||
# Attach metadata node
|
|
||||||
elem.append(common.MetadataTemplate())
|
|
||||||
|
|
||||||
|
|
||||||
class AvailabilityZonesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('availabilityZones')
|
|
||||||
zoneElem = xmlutil.SubTemplateElement(root, 'availabilityZone',
|
|
||||||
selector='availabilityZoneInfo')
|
|
||||||
make_availability_zone(zoneElem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap={
|
|
||||||
Availability_zone.alias: Availability_zone.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class AvailabilityZoneController(wsgi.Controller):
|
class AvailabilityZoneController(wsgi.Controller):
|
||||||
"""The Availability Zone API controller for the OpenStack API."""
|
"""The Availability Zone API controller for the OpenStack API."""
|
||||||
|
|
||||||
|
@ -139,7 +100,6 @@ class AvailabilityZoneController(wsgi.Controller):
|
||||||
"hosts": None})
|
"hosts": None})
|
||||||
return {'availabilityZoneInfo': result}
|
return {'availabilityZoneInfo': result}
|
||||||
|
|
||||||
@wsgi.serializers(xml=AvailabilityZonesTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Returns a summary list of availability zone."""
|
"""Returns a summary list of availability zone."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -147,7 +107,6 @@ class AvailabilityZoneController(wsgi.Controller):
|
||||||
|
|
||||||
return self._describe_availability_zones(context)
|
return self._describe_availability_zones(context)
|
||||||
|
|
||||||
@wsgi.serializers(xml=AvailabilityZonesTemplate)
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Returns a detailed list of availability zone."""
|
"""Returns a detailed list of availability zone."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -21,7 +21,6 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
@ -58,25 +57,6 @@ CONF.import_opt('compute_driver', 'nova.virt.driver')
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _interface_dict(interface_ref):
|
|
||||||
d = {}
|
|
||||||
for f in interface_fields:
|
|
||||||
d[f] = interface_ref.get(f)
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def _make_node_elem(elem):
|
|
||||||
for f in node_fields:
|
|
||||||
elem.set(f)
|
|
||||||
for f in node_ext_fields:
|
|
||||||
elem.set(f)
|
|
||||||
|
|
||||||
|
|
||||||
def _make_interface_elem(elem):
|
|
||||||
for f in interface_fields:
|
|
||||||
elem.set(f)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_ironic_client():
|
def _get_ironic_client():
|
||||||
"""return an Ironic client."""
|
"""return an Ironic client."""
|
||||||
# TODO(NobodyCam): Fix insecure setting
|
# TODO(NobodyCam): Fix insecure setting
|
||||||
|
@ -99,31 +79,6 @@ def _no_ironic_proxy(cmd):
|
||||||
"action.") % {'cmd': cmd})
|
"action.") % {'cmd': cmd})
|
||||||
|
|
||||||
|
|
||||||
class NodeTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
node_elem = xmlutil.TemplateElement('node', selector='node')
|
|
||||||
_make_node_elem(node_elem)
|
|
||||||
ifs_elem = xmlutil.TemplateElement('interfaces')
|
|
||||||
if_elem = xmlutil.SubTemplateElement(ifs_elem, 'interface',
|
|
||||||
selector='interfaces')
|
|
||||||
_make_interface_elem(if_elem)
|
|
||||||
node_elem.append(ifs_elem)
|
|
||||||
return xmlutil.MasterTemplate(node_elem, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class NodesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('nodes')
|
|
||||||
node_elem = xmlutil.SubTemplateElement(root, 'node', selector='nodes')
|
|
||||||
_make_node_elem(node_elem)
|
|
||||||
ifs_elem = xmlutil.TemplateElement('interfaces')
|
|
||||||
if_elem = xmlutil.SubTemplateElement(ifs_elem, 'interface',
|
|
||||||
selector='interfaces')
|
|
||||||
_make_interface_elem(if_elem)
|
|
||||||
node_elem.append(ifs_elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class BareMetalNodeController(wsgi.Controller):
|
class BareMetalNodeController(wsgi.Controller):
|
||||||
"""The Bare-Metal Node API controller for the OpenStack API.
|
"""The Bare-Metal Node API controller for the OpenStack API.
|
||||||
|
|
||||||
|
@ -145,7 +100,6 @@ class BareMetalNodeController(wsgi.Controller):
|
||||||
d[f] = node_ref.get(f)
|
d[f] = node_ref.get(f)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@wsgi.serializers(xml=NodesTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -164,7 +118,6 @@ class BareMetalNodeController(wsgi.Controller):
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
return {'nodes': nodes}
|
return {'nodes': nodes}
|
||||||
|
|
||||||
@wsgi.serializers(xml=NodeTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -184,7 +137,6 @@ class BareMetalNodeController(wsgi.Controller):
|
||||||
node['interfaces'].append({'address': port.address})
|
node['interfaces'].append({'address': port.address})
|
||||||
return {'node': node}
|
return {'node': node}
|
||||||
|
|
||||||
@wsgi.serializers(xml=NodeTemplate)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
_no_ironic_proxy("port-create")
|
_no_ironic_proxy("port-create")
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ from webob import exc
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.cells import rpcapi as cells_rpcapi
|
from nova.cells import rpcapi as cells_rpcapi
|
||||||
from nova.compute import api as compute
|
from nova.compute import api as compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
@ -41,95 +40,9 @@ CONF.import_opt('capabilities', 'nova.cells.opts', group='cells')
|
||||||
authorize = extensions.extension_authorizer('compute', 'cells')
|
authorize = extensions.extension_authorizer('compute', 'cells')
|
||||||
|
|
||||||
|
|
||||||
def make_cell(elem):
|
|
||||||
elem.set('name')
|
|
||||||
elem.set('username')
|
|
||||||
elem.set('type')
|
|
||||||
elem.set('rpc_host')
|
|
||||||
elem.set('rpc_port')
|
|
||||||
|
|
||||||
caps = xmlutil.SubTemplateElement(elem, 'capabilities',
|
|
||||||
selector='capabilities')
|
|
||||||
cap = xmlutil.SubTemplateElement(caps, xmlutil.Selector(0),
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
cap.text = 1
|
|
||||||
make_capacity(elem)
|
|
||||||
|
|
||||||
|
|
||||||
def make_capacity(cell):
|
|
||||||
|
|
||||||
def get_units_by_mb(capacity_info):
|
|
||||||
return capacity_info['units_by_mb'].items()
|
|
||||||
|
|
||||||
capacity = xmlutil.SubTemplateElement(cell, 'capacities',
|
|
||||||
selector='capacities')
|
|
||||||
|
|
||||||
ram_free = xmlutil.SubTemplateElement(capacity, 'ram_free',
|
|
||||||
selector='ram_free')
|
|
||||||
ram_free.set('total_mb', 'total_mb')
|
|
||||||
unit_by_mb = xmlutil.SubTemplateElement(ram_free, 'unit_by_mb',
|
|
||||||
selector=get_units_by_mb)
|
|
||||||
unit_by_mb.set('mb', 0)
|
|
||||||
unit_by_mb.set('unit', 1)
|
|
||||||
|
|
||||||
disk_free = xmlutil.SubTemplateElement(capacity, 'disk_free',
|
|
||||||
selector='disk_free')
|
|
||||||
disk_free.set('total_mb', 'total_mb')
|
|
||||||
unit_by_mb = xmlutil.SubTemplateElement(disk_free, 'unit_by_mb',
|
|
||||||
selector=get_units_by_mb)
|
|
||||||
unit_by_mb.set('mb', 0)
|
|
||||||
unit_by_mb.set('unit', 1)
|
|
||||||
|
|
||||||
cell_nsmap = {None: wsgi.XMLNS_V10}
|
cell_nsmap = {None: wsgi.XMLNS_V10}
|
||||||
|
|
||||||
|
|
||||||
class CellTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('cell', selector='cell')
|
|
||||||
make_cell(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=cell_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class CellsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('cells')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'cell', selector='cells')
|
|
||||||
make_cell(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=cell_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class CellDeserializer(wsgi.XMLDeserializer):
|
|
||||||
"""Deserializer to handle xml-formatted cell create requests."""
|
|
||||||
|
|
||||||
def _extract_capabilities(self, cap_node):
|
|
||||||
caps = {}
|
|
||||||
for cap in cap_node.childNodes:
|
|
||||||
cap_name = cap.tagName
|
|
||||||
caps[cap_name] = self.extract_text(cap)
|
|
||||||
return caps
|
|
||||||
|
|
||||||
def _extract_cell(self, node):
|
|
||||||
cell = {}
|
|
||||||
cell_node = self.find_first_child_named(node, 'cell')
|
|
||||||
|
|
||||||
extract_fns = {
|
|
||||||
'capabilities': self._extract_capabilities,
|
|
||||||
'rpc_port': lambda child: int(self.extract_text(child)),
|
|
||||||
}
|
|
||||||
|
|
||||||
for child in cell_node.childNodes:
|
|
||||||
name = child.tagName
|
|
||||||
extract_fn = extract_fns.get(name, self.extract_text)
|
|
||||||
cell[name] = extract_fn(child)
|
|
||||||
return cell
|
|
||||||
|
|
||||||
def default(self, string):
|
|
||||||
"""Deserialize an xml-formatted cell create request."""
|
|
||||||
node = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
|
|
||||||
return {'body': {'cell': self._extract_cell(node)}}
|
|
||||||
|
|
||||||
|
|
||||||
def _filter_keys(item, keys):
|
def _filter_keys(item, keys):
|
||||||
"""Filters all model attributes except for keys
|
"""Filters all model attributes except for keys
|
||||||
item is a dict
|
item is a dict
|
||||||
|
@ -197,7 +110,6 @@ class Controller(object):
|
||||||
items = [_scrub_cell(item, detail=detail) for item in items]
|
items = [_scrub_cell(item, detail=detail) for item in items]
|
||||||
return dict(cells=items)
|
return dict(cells=items)
|
||||||
|
|
||||||
@wsgi.serializers(xml=CellsTemplate)
|
|
||||||
@common.check_cells_enabled
|
@common.check_cells_enabled
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return all cells in brief."""
|
"""Return all cells in brief."""
|
||||||
|
@ -205,7 +117,6 @@ class Controller(object):
|
||||||
authorize(ctxt)
|
authorize(ctxt)
|
||||||
return self._get_cells(ctxt, req)
|
return self._get_cells(ctxt, req)
|
||||||
|
|
||||||
@wsgi.serializers(xml=CellsTemplate)
|
|
||||||
@common.check_cells_enabled
|
@common.check_cells_enabled
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Return all cells in detail."""
|
"""Return all cells in detail."""
|
||||||
|
@ -213,7 +124,6 @@ class Controller(object):
|
||||||
authorize(ctxt)
|
authorize(ctxt)
|
||||||
return self._get_cells(ctxt, req, detail=True)
|
return self._get_cells(ctxt, req, detail=True)
|
||||||
|
|
||||||
@wsgi.serializers(xml=CellTemplate)
|
|
||||||
@common.check_cells_enabled
|
@common.check_cells_enabled
|
||||||
def info(self, req):
|
def info(self, req):
|
||||||
"""Return name and capabilities for this cell."""
|
"""Return name and capabilities for this cell."""
|
||||||
|
@ -232,7 +142,6 @@ class Controller(object):
|
||||||
'capabilities': cell_capabs}
|
'capabilities': cell_capabs}
|
||||||
return dict(cell=cell)
|
return dict(cell=cell)
|
||||||
|
|
||||||
@wsgi.serializers(xml=CellTemplate)
|
|
||||||
@common.check_cells_enabled
|
@common.check_cells_enabled
|
||||||
def capacities(self, req, id=None):
|
def capacities(self, req, id=None):
|
||||||
"""Return capacities for a given cell or all cells."""
|
"""Return capacities for a given cell or all cells."""
|
||||||
|
@ -252,7 +161,6 @@ class Controller(object):
|
||||||
|
|
||||||
return dict(cell={"capacities": capacities})
|
return dict(cell={"capacities": capacities})
|
||||||
|
|
||||||
@wsgi.serializers(xml=CellTemplate)
|
|
||||||
@common.check_cells_enabled
|
@common.check_cells_enabled
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return data about the given cell name. 'id' is a cell name."""
|
"""Return data about the given cell name. 'id' is a cell name."""
|
||||||
|
@ -344,8 +252,6 @@ class Controller(object):
|
||||||
# Now set the transport URL
|
# Now set the transport URL
|
||||||
cell['transport_url'] = str(transport_url)
|
cell['transport_url'] = str(transport_url)
|
||||||
|
|
||||||
@wsgi.serializers(xml=CellTemplate)
|
|
||||||
@wsgi.deserializers(xml=CellDeserializer)
|
|
||||||
@common.check_cells_enabled
|
@common.check_cells_enabled
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Create a child cell entry."""
|
"""Create a child cell entry."""
|
||||||
|
@ -369,8 +275,6 @@ class Controller(object):
|
||||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||||
return dict(cell=_scrub_cell(cell))
|
return dict(cell=_scrub_cell(cell))
|
||||||
|
|
||||||
@wsgi.serializers(xml=CellTemplate)
|
|
||||||
@wsgi.deserializers(xml=CellDeserializer)
|
|
||||||
@common.check_cells_enabled
|
@common.check_cells_enabled
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
"""Update a child cell entry. 'id' is the cell name to update."""
|
"""Update a child cell entry. 'id' is the cell name to update."""
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
import nova.cert.rpcapi
|
import nova.cert.rpcapi
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -24,19 +22,6 @@ from nova.i18n import _
|
||||||
authorize = extensions.extension_authorizer('compute', 'certificates')
|
authorize = extensions.extension_authorizer('compute', 'certificates')
|
||||||
|
|
||||||
|
|
||||||
def make_certificate(elem):
|
|
||||||
elem.set('data')
|
|
||||||
elem.set('private_key')
|
|
||||||
|
|
||||||
|
|
||||||
class CertificateTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('certificate',
|
|
||||||
selector='certificate')
|
|
||||||
make_certificate(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
def _translate_certificate_view(certificate, private_key=None):
|
def _translate_certificate_view(certificate, private_key=None):
|
||||||
return {
|
return {
|
||||||
'data': certificate,
|
'data': certificate,
|
||||||
|
@ -51,7 +36,6 @@ class CertificatesController(object):
|
||||||
self.cert_rpcapi = nova.cert.rpcapi.CertAPI()
|
self.cert_rpcapi = nova.cert.rpcapi.CertAPI()
|
||||||
super(CertificatesController, self).__init__()
|
super(CertificatesController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=CertificateTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return certificate information."""
|
"""Return certificate information."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -66,7 +50,6 @@ class CertificatesController(object):
|
||||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
||||||
return {'certificate': _translate_certificate_view(cert)}
|
return {'certificate': _translate_certificate_view(cert)}
|
||||||
|
|
||||||
@wsgi.serializers(xml=CertificateTemplate)
|
|
||||||
def create(self, req, body=None):
|
def create(self, req, body=None):
|
||||||
"""Create a certificate."""
|
"""Create a certificate."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -19,8 +19,6 @@ from oslo.utils import timeutils
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.cloudpipe import pipelib
|
from nova.cloudpipe import pipelib
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova.compute import utils as compute_utils
|
from nova.compute import utils as compute_utils
|
||||||
|
@ -37,26 +35,6 @@ CONF.import_opt('keys_path', 'nova.crypto')
|
||||||
authorize = extensions.extension_authorizer('compute', 'cloudpipe')
|
authorize = extensions.extension_authorizer('compute', 'cloudpipe')
|
||||||
|
|
||||||
|
|
||||||
class CloudpipeTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('cloudpipe')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'instance_id',
|
|
||||||
selector='instance_id')
|
|
||||||
elem.text = str
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class CloudpipesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('cloudpipes')
|
|
||||||
elem1 = xmlutil.SubTemplateElement(root, 'cloudpipe',
|
|
||||||
selector='cloudpipes')
|
|
||||||
elem2 = xmlutil.SubTemplateElement(elem1, xmlutil.Selector(0),
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
elem2.text = 1
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class CloudpipeController(object):
|
class CloudpipeController(object):
|
||||||
"""Handle creating and listing cloudpipe instances."""
|
"""Handle creating and listing cloudpipe instances."""
|
||||||
|
|
||||||
|
@ -126,7 +104,6 @@ class CloudpipeController(object):
|
||||||
rv['state'] = 'invalid'
|
rv['state'] = 'invalid'
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
@wsgi.serializers(xml=CloudpipeTemplate)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Create a new cloudpipe instance, if none exists.
|
"""Create a new cloudpipe instance, if none exists.
|
||||||
|
|
||||||
|
@ -154,7 +131,6 @@ class CloudpipeController(object):
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
return {'instance_id': instance['uuid']}
|
return {'instance_id': instance['uuid']}
|
||||||
|
|
||||||
@wsgi.serializers(xml=CloudpipesTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""List running cloudpipe instances."""
|
"""List running cloudpipe instances."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -18,26 +18,10 @@
|
||||||
from nova.api.openstack.compute import servers
|
from nova.api.openstack.compute import servers
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'config_drive')
|
authorize = extensions.soft_extension_authorizer('compute', 'config_drive')
|
||||||
|
|
||||||
|
|
||||||
class ServerConfigDriveTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server')
|
|
||||||
root.set('config_drive', 'config_drive')
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ServersConfigDriveTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
elem.set('config_drive', 'config_drive')
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(servers.Controller):
|
class Controller(servers.Controller):
|
||||||
|
|
||||||
def _add_config_drive(self, req, servers):
|
def _add_config_drive(self, req, servers):
|
||||||
|
@ -49,7 +33,7 @@ class Controller(servers.Controller):
|
||||||
|
|
||||||
def _show(self, req, resp_obj):
|
def _show(self, req, resp_obj):
|
||||||
if 'server' in resp_obj.obj:
|
if 'server' in resp_obj.obj:
|
||||||
resp_obj.attach(xml=ServerConfigDriveTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
self._add_config_drive(req, [server])
|
self._add_config_drive(req, [server])
|
||||||
|
|
||||||
|
@ -63,7 +47,7 @@ class Controller(servers.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if 'servers' in resp_obj.obj and authorize(context):
|
if 'servers' in resp_obj.obj and authorize(context):
|
||||||
resp_obj.attach(xml=ServersConfigDriveTemplate())
|
resp_obj.attach()
|
||||||
servers = resp_obj.obj['servers']
|
servers = resp_obj.obj['servers']
|
||||||
self._add_config_drive(req, servers)
|
self._add_config_drive(req, servers)
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
|
||||||
ALIAS = 'OS-DCF'
|
ALIAS = 'OS-DCF'
|
||||||
|
@ -43,21 +42,6 @@ def disk_config_from_api(value):
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
|
|
||||||
class ImageDiskConfigTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('image')
|
|
||||||
root.set('{%s}diskConfig' % XMLNS_DCF, API_DISK_CONFIG)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
|
|
||||||
|
|
||||||
|
|
||||||
class ImagesDiskConfigTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('images')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'image', selector='images')
|
|
||||||
elem.set('{%s}diskConfig' % XMLNS_DCF, API_DISK_CONFIG)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
|
|
||||||
|
|
||||||
|
|
||||||
class ImageDiskConfigController(wsgi.Controller):
|
class ImageDiskConfigController(wsgi.Controller):
|
||||||
def _add_disk_config(self, context, images):
|
def _add_disk_config(self, context, images):
|
||||||
for image in images:
|
for image in images:
|
||||||
|
@ -71,7 +55,7 @@ class ImageDiskConfigController(wsgi.Controller):
|
||||||
def show(self, req, resp_obj, id):
|
def show(self, req, resp_obj, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if 'image' in resp_obj.obj and authorize(context):
|
if 'image' in resp_obj.obj and authorize(context):
|
||||||
resp_obj.attach(xml=ImageDiskConfigTemplate())
|
resp_obj.attach()
|
||||||
image = resp_obj.obj['image']
|
image = resp_obj.obj['image']
|
||||||
self._add_disk_config(context, [image])
|
self._add_disk_config(context, [image])
|
||||||
|
|
||||||
|
@ -79,26 +63,11 @@ class ImageDiskConfigController(wsgi.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if 'images' in resp_obj.obj and authorize(context):
|
if 'images' in resp_obj.obj and authorize(context):
|
||||||
resp_obj.attach(xml=ImagesDiskConfigTemplate())
|
resp_obj.attach()
|
||||||
images = resp_obj.obj['images']
|
images = resp_obj.obj['images']
|
||||||
self._add_disk_config(context, images)
|
self._add_disk_config(context, images)
|
||||||
|
|
||||||
|
|
||||||
class ServerDiskConfigTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server')
|
|
||||||
root.set('{%s}diskConfig' % XMLNS_DCF, API_DISK_CONFIG)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
|
|
||||||
|
|
||||||
|
|
||||||
class ServersDiskConfigTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
elem.set('{%s}diskConfig' % XMLNS_DCF, API_DISK_CONFIG)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
|
|
||||||
|
|
||||||
|
|
||||||
class ServerDiskConfigController(wsgi.Controller):
|
class ServerDiskConfigController(wsgi.Controller):
|
||||||
def _add_disk_config(self, req, servers):
|
def _add_disk_config(self, req, servers):
|
||||||
for server in servers:
|
for server in servers:
|
||||||
|
@ -110,7 +79,7 @@ class ServerDiskConfigController(wsgi.Controller):
|
||||||
|
|
||||||
def _show(self, req, resp_obj):
|
def _show(self, req, resp_obj):
|
||||||
if 'server' in resp_obj.obj:
|
if 'server' in resp_obj.obj:
|
||||||
resp_obj.attach(xml=ServerDiskConfigTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
self._add_disk_config(req, [server])
|
self._add_disk_config(req, [server])
|
||||||
|
|
||||||
|
@ -124,7 +93,7 @@ class ServerDiskConfigController(wsgi.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if 'servers' in resp_obj.obj and authorize(context):
|
if 'servers' in resp_obj.obj and authorize(context):
|
||||||
resp_obj.attach(xml=ServersDiskConfigTemplate())
|
resp_obj.attach()
|
||||||
servers = resp_obj.obj['servers']
|
servers = resp_obj.obj['servers']
|
||||||
self._add_disk_config(req, servers)
|
self._add_disk_config(req, servers)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import availability_zones as avail_zone
|
from nova import availability_zones as avail_zone
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute',
|
authorize = extensions.soft_extension_authorizer('compute',
|
||||||
|
@ -39,7 +38,7 @@ class ExtendedAZController(wsgi.Controller):
|
||||||
def show(self, req, resp_obj, id):
|
def show(self, req, resp_obj, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
resp_obj.attach(xml=ExtendedAZTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
self._extend_server(context, server, db_instance)
|
self._extend_server(context, server, db_instance)
|
||||||
|
@ -48,7 +47,7 @@ class ExtendedAZController(wsgi.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
resp_obj.attach(xml=ExtendedAZsTemplate())
|
resp_obj.attach()
|
||||||
servers = list(resp_obj.obj['servers'])
|
servers = list(resp_obj.obj['servers'])
|
||||||
for server in servers:
|
for server in servers:
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
|
@ -68,27 +67,3 @@ class Extended_availability_zone(extensions.ExtensionDescriptor):
|
||||||
controller = ExtendedAZController()
|
controller = ExtendedAZController()
|
||||||
extension = extensions.ControllerExtension(self, 'servers', controller)
|
extension = extensions.ControllerExtension(self, 'servers', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem):
|
|
||||||
elem.set('{%s}availability_zone' % Extended_availability_zone.namespace,
|
|
||||||
'%s:availability_zone' % Extended_availability_zone.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedAZTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server', selector='server')
|
|
||||||
make_server(root)
|
|
||||||
alias = Extended_availability_zone.alias
|
|
||||||
namespace = Extended_availability_zone.namespace
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedAZsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
alias = Extended_availability_zone.alias
|
|
||||||
namespace = Extended_availability_zone.namespace
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
|
||||||
|
|
|
@ -17,10 +17,8 @@
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack.compute import ips
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'extended_ips')
|
authorize = extensions.soft_extension_authorizer('compute', 'extended_ips')
|
||||||
|
@ -48,7 +46,7 @@ class ExtendedIpsController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedIpsServerTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
# server['id'] is guaranteed to be in the cache due to
|
# server['id'] is guaranteed to be in the cache due to
|
||||||
|
@ -60,7 +58,7 @@ class ExtendedIpsController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedIpsServersTemplate())
|
resp_obj.attach()
|
||||||
servers = list(resp_obj.obj['servers'])
|
servers = list(resp_obj.obj['servers'])
|
||||||
for server in servers:
|
for server in servers:
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
|
@ -82,28 +80,3 @@ class Extended_ips(extensions.ExtensionDescriptor):
|
||||||
controller = ExtendedIpsController()
|
controller = ExtendedIpsController()
|
||||||
extension = extensions.ControllerExtension(self, 'servers', controller)
|
extension = extensions.ControllerExtension(self, 'servers', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem):
|
|
||||||
elem.append(ips.AddressesTemplate())
|
|
||||||
ip = elem['addresses']['network']['ip']
|
|
||||||
ip.set('{%s}type' % Extended_ips.namespace,
|
|
||||||
'%s:type' % Extended_ips.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedIpsServerTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server', selector='server')
|
|
||||||
xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Extended_ips.alias: Extended_ips.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedIpsServersTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Extended_ips.alias: Extended_ips.namespace})
|
|
||||||
|
|
|
@ -17,10 +17,8 @@
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack.compute import ips
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'extended_ips_mac')
|
authorize = extensions.soft_extension_authorizer('compute', 'extended_ips_mac')
|
||||||
|
|
||||||
|
@ -46,7 +44,7 @@ class ExtendedIpsMacController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedIpsMacServerTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
# server['id'] is guaranteed to be in the cache due to
|
# server['id'] is guaranteed to be in the cache due to
|
||||||
|
@ -58,7 +56,7 @@ class ExtendedIpsMacController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedIpsMacServersTemplate())
|
resp_obj.attach()
|
||||||
servers = list(resp_obj.obj['servers'])
|
servers = list(resp_obj.obj['servers'])
|
||||||
for server in servers:
|
for server in servers:
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
|
@ -80,27 +78,3 @@ class Extended_ips_mac(extensions.ExtensionDescriptor):
|
||||||
controller = ExtendedIpsMacController()
|
controller = ExtendedIpsMacController()
|
||||||
extension = extensions.ControllerExtension(self, 'servers', controller)
|
extension = extensions.ControllerExtension(self, 'servers', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem):
|
|
||||||
elem.append(ips.AddressesTemplate())
|
|
||||||
ip = elem['addresses']['network']['ip']
|
|
||||||
ip.set('{%s}mac_addr' % Extended_ips_mac.namespace,
|
|
||||||
'%s:mac_addr' % Extended_ips_mac.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedIpsMacServerTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server', selector='server')
|
|
||||||
make_server(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Extended_ips_mac.alias: Extended_ips_mac.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedIpsMacServersTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Extended_ips_mac.alias: Extended_ips_mac.namespace})
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute',
|
authorize = extensions.soft_extension_authorizer('compute',
|
||||||
'extended_server_attributes')
|
'extended_server_attributes')
|
||||||
|
@ -40,7 +39,7 @@ class ExtendedServerAttributesController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedServerAttributeTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
# server['id'] is guaranteed to be in the cache due to
|
# server['id'] is guaranteed to be in the cache due to
|
||||||
|
@ -52,7 +51,7 @@ class ExtendedServerAttributesController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedServerAttributesTemplate())
|
resp_obj.attach()
|
||||||
|
|
||||||
servers = list(resp_obj.obj['servers'])
|
servers = list(resp_obj.obj['servers'])
|
||||||
for server in servers:
|
for server in servers:
|
||||||
|
@ -75,31 +74,3 @@ class Extended_server_attributes(extensions.ExtensionDescriptor):
|
||||||
controller = ExtendedServerAttributesController()
|
controller = ExtendedServerAttributesController()
|
||||||
extension = extensions.ControllerExtension(self, 'servers', controller)
|
extension = extensions.ControllerExtension(self, 'servers', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem):
|
|
||||||
elem.set('{%s}instance_name' % Extended_server_attributes.namespace,
|
|
||||||
'%s:instance_name' % Extended_server_attributes.alias)
|
|
||||||
elem.set('{%s}host' % Extended_server_attributes.namespace,
|
|
||||||
'%s:host' % Extended_server_attributes.alias)
|
|
||||||
elem.set('{%s}hypervisor_hostname' % Extended_server_attributes.namespace,
|
|
||||||
'%s:hypervisor_hostname' % Extended_server_attributes.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedServerAttributeTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server', selector='server')
|
|
||||||
make_server(root)
|
|
||||||
alias = Extended_server_attributes.alias
|
|
||||||
namespace = Extended_server_attributes.namespace
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedServerAttributesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
alias = Extended_server_attributes.alias
|
|
||||||
namespace = Extended_server_attributes.namespace
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'extended_status')
|
authorize = extensions.soft_extension_authorizer('compute', 'extended_status')
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ class ExtendedStatusController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedStatusTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
# server['id'] is guaranteed to be in the cache due to
|
# server['id'] is guaranteed to be in the cache due to
|
||||||
|
@ -47,7 +46,7 @@ class ExtendedStatusController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedStatusesTemplate())
|
resp_obj.attach()
|
||||||
servers = list(resp_obj.obj['servers'])
|
servers = list(resp_obj.obj['servers'])
|
||||||
for server in servers:
|
for server in servers:
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
|
@ -69,29 +68,3 @@ class Extended_status(extensions.ExtensionDescriptor):
|
||||||
controller = ExtendedStatusController()
|
controller = ExtendedStatusController()
|
||||||
extension = extensions.ControllerExtension(self, 'servers', controller)
|
extension = extensions.ControllerExtension(self, 'servers', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem):
|
|
||||||
elem.set('{%s}task_state' % Extended_status.namespace,
|
|
||||||
'%s:task_state' % Extended_status.alias)
|
|
||||||
elem.set('{%s}power_state' % Extended_status.namespace,
|
|
||||||
'%s:power_state' % Extended_status.alias)
|
|
||||||
elem.set('{%s}vm_state' % Extended_status.namespace,
|
|
||||||
'%s:vm_state' % Extended_status.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedStatusTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server', selector='server')
|
|
||||||
make_server(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Extended_status.alias: Extended_status.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedStatusesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Extended_status.alias: Extended_status.namespace})
|
|
||||||
|
|
|
@ -15,29 +15,11 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import network
|
from nova import network
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'extended_vif_net')
|
authorize = extensions.soft_extension_authorizer('compute', 'extended_vif_net')
|
||||||
|
|
||||||
|
|
||||||
def make_vif(elem):
|
|
||||||
elem.set('{%s}net_id' % Extended_virtual_interfaces_net.namespace,
|
|
||||||
'%s:net_id' % Extended_virtual_interfaces_net.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedVirtualInterfaceNetTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('virtual_interfaces',
|
|
||||||
selector='virtual_interfaces')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'virtual_interface',
|
|
||||||
selector='virtual_interfaces')
|
|
||||||
make_vif(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1,
|
|
||||||
nsmap={Extended_virtual_interfaces_net.alias:
|
|
||||||
Extended_virtual_interfaces_net.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedServerVIFNetController(wsgi.Controller):
|
class ExtendedServerVIFNetController(wsgi.Controller):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(ExtendedServerVIFNetController, self).__init__()
|
super(ExtendedServerVIFNetController, self).__init__()
|
||||||
|
@ -49,7 +31,7 @@ class ExtendedServerVIFNetController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedVirtualInterfaceNetTemplate())
|
resp_obj.attach()
|
||||||
for vif in resp_obj.obj['virtual_interfaces']:
|
for vif in resp_obj.obj['virtual_interfaces']:
|
||||||
vif1 = self.network_api.get_vif_by_mac_address(context,
|
vif1 = self.network_api.get_vif_by_mac_address(context,
|
||||||
vif['mac_address'])
|
vif['mac_address'])
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import objects
|
from nova import objects
|
||||||
|
|
||||||
|
@ -40,7 +39,7 @@ class ExtendedVolumesController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedVolumesServerTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
# server['id'] is guaranteed to be in the cache due to
|
# server['id'] is guaranteed to be in the cache due to
|
||||||
|
@ -52,7 +51,7 @@ class ExtendedVolumesController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedVolumesServersTemplate())
|
resp_obj.attach()
|
||||||
servers = list(resp_obj.obj['servers'])
|
servers = list(resp_obj.obj['servers'])
|
||||||
for server in servers:
|
for server in servers:
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
|
@ -77,27 +76,3 @@ class Extended_volumes(extensions.ExtensionDescriptor):
|
||||||
|
|
||||||
def get_resources(self):
|
def get_resources(self):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem):
|
|
||||||
volumes = xmlutil.SubTemplateElement(
|
|
||||||
elem, '{%s}volume_attached' % Extended_volumes.namespace,
|
|
||||||
selector='%s:volumes_attached' % Extended_volumes.alias)
|
|
||||||
volumes.set('id')
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedVolumesServerTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server', selector='server')
|
|
||||||
make_server(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Extended_volumes.alias: Extended_volumes.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedVolumesServersTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Extended_volumes.alias: Extended_volumes.namespace})
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import objects
|
from nova import objects
|
||||||
|
@ -30,44 +29,6 @@ soft_authorize = extensions.soft_extension_authorizer('compute',
|
||||||
authorize = extensions.extension_authorizer('compute', 'flavor_access')
|
authorize = extensions.extension_authorizer('compute', 'flavor_access')
|
||||||
|
|
||||||
|
|
||||||
def make_flavor(elem):
|
|
||||||
elem.set('{%s}is_public' % Flavor_access.namespace,
|
|
||||||
'%s:is_public' % Flavor_access.alias)
|
|
||||||
|
|
||||||
|
|
||||||
def make_flavor_access(elem):
|
|
||||||
elem.set('flavor_id')
|
|
||||||
elem.set('tenant_id')
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavor', selector='flavor')
|
|
||||||
make_flavor(root)
|
|
||||||
alias = Flavor_access.alias
|
|
||||||
namespace = Flavor_access.namespace
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'flavor', selector='flavors')
|
|
||||||
make_flavor(elem)
|
|
||||||
alias = Flavor_access.alias
|
|
||||||
namespace = Flavor_access.namespace
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorAccessTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavor_access')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'access',
|
|
||||||
selector='flavor_access')
|
|
||||||
make_flavor_access(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
def _marshall_flavor_access(flavor):
|
def _marshall_flavor_access(flavor):
|
||||||
rval = []
|
rval = []
|
||||||
for project_id in flavor.projects:
|
for project_id in flavor.projects:
|
||||||
|
@ -83,7 +44,6 @@ class FlavorAccessController(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(FlavorAccessController, self).__init__()
|
super(FlavorAccessController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=FlavorAccessTemplate)
|
|
||||||
def index(self, req, flavor_id):
|
def index(self, req, flavor_id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -128,7 +88,7 @@ class FlavorActionController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if soft_authorize(context):
|
if soft_authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=FlavorTemplate())
|
resp_obj.attach()
|
||||||
db_flavor = req.get_db_flavor(id)
|
db_flavor = req.get_db_flavor(id)
|
||||||
|
|
||||||
self._extend_flavor(resp_obj.obj['flavor'], db_flavor)
|
self._extend_flavor(resp_obj.obj['flavor'], db_flavor)
|
||||||
|
@ -138,7 +98,7 @@ class FlavorActionController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if soft_authorize(context):
|
if soft_authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=FlavorsTemplate())
|
resp_obj.attach()
|
||||||
|
|
||||||
flavors = list(resp_obj.obj['flavors'])
|
flavors = list(resp_obj.obj['flavors'])
|
||||||
for flavor_rval in flavors:
|
for flavor_rval in flavors:
|
||||||
|
@ -150,13 +110,12 @@ class FlavorActionController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if soft_authorize(context):
|
if soft_authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=FlavorTemplate())
|
resp_obj.attach()
|
||||||
|
|
||||||
db_flavor = req.get_db_flavor(resp_obj.obj['flavor']['id'])
|
db_flavor = req.get_db_flavor(resp_obj.obj['flavor']['id'])
|
||||||
|
|
||||||
self._extend_flavor(resp_obj.obj['flavor'], db_flavor)
|
self._extend_flavor(resp_obj.obj['flavor'], db_flavor)
|
||||||
|
|
||||||
@wsgi.serializers(xml=FlavorAccessTemplate)
|
|
||||||
@wsgi.action("addTenantAccess")
|
@wsgi.action("addTenantAccess")
|
||||||
def _addTenantAccess(self, req, id, body):
|
def _addTenantAccess(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -180,7 +139,6 @@ class FlavorActionController(wsgi.Controller):
|
||||||
|
|
||||||
return _marshall_flavor_access(flavor)
|
return _marshall_flavor_access(flavor)
|
||||||
|
|
||||||
@wsgi.serializers(xml=FlavorAccessTemplate)
|
|
||||||
@wsgi.action("removeTenantAccess")
|
@wsgi.action("removeTenantAccess")
|
||||||
def _removeTenantAccess(self, req, id, body):
|
def _removeTenantAccess(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'flavor_disabled')
|
authorize = extensions.soft_extension_authorizer('compute', 'flavor_disabled')
|
||||||
|
@ -33,7 +32,7 @@ class FlavorDisabledController(wsgi.Controller):
|
||||||
if not authorize(req.environ['nova.context']):
|
if not authorize(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
if 'flavor' in resp_obj.obj:
|
if 'flavor' in resp_obj.obj:
|
||||||
resp_obj.attach(xml=FlavorDisabledTemplate())
|
resp_obj.attach()
|
||||||
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
|
@ -48,7 +47,7 @@ class FlavorDisabledController(wsgi.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
if not authorize(req.environ['nova.context']):
|
if not authorize(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
resp_obj.attach(xml=FlavorsDisabledTemplate())
|
resp_obj.attach()
|
||||||
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,25 +64,3 @@ class Flavor_disabled(extensions.ExtensionDescriptor):
|
||||||
controller = FlavorDisabledController()
|
controller = FlavorDisabledController()
|
||||||
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_flavor(elem):
|
|
||||||
elem.set('{%s}disabled' % Flavor_disabled.namespace,
|
|
||||||
'%s:disabled' % Flavor_disabled.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorDisabledTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavor', selector='flavor')
|
|
||||||
make_flavor(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Flavor_disabled.alias: Flavor_disabled.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorsDisabledTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'flavor', selector='flavors')
|
|
||||||
make_flavor(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Flavor_disabled.alias: Flavor_disabled.namespace})
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'flavor_rxtx')
|
authorize = extensions.soft_extension_authorizer('compute', 'flavor_rxtx')
|
||||||
|
@ -33,7 +32,7 @@ class FlavorRxtxController(wsgi.Controller):
|
||||||
if not authorize(req.environ['nova.context']):
|
if not authorize(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
if 'flavor' in resp_obj.obj:
|
if 'flavor' in resp_obj.obj:
|
||||||
resp_obj.attach(xml=FlavorRxtxTemplate())
|
resp_obj.attach()
|
||||||
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
|
@ -48,7 +47,7 @@ class FlavorRxtxController(wsgi.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
if not authorize(req.environ['nova.context']):
|
if not authorize(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
resp_obj.attach(xml=FlavorsRxtxTemplate())
|
resp_obj.attach()
|
||||||
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,23 +64,3 @@ class Flavor_rxtx(extensions.ExtensionDescriptor):
|
||||||
controller = FlavorRxtxController()
|
controller = FlavorRxtxController()
|
||||||
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_flavor(elem):
|
|
||||||
# NOTE(vish): this was originally added without a namespace
|
|
||||||
elem.set('rxtx_factor', xmlutil.EmptyStringSelector('rxtx_factor'))
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorRxtxTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavor', selector='flavor')
|
|
||||||
make_flavor(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorsRxtxTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'flavor', selector='flavors')
|
|
||||||
make_flavor(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'flavor_swap')
|
authorize = extensions.soft_extension_authorizer('compute', 'flavor_swap')
|
||||||
|
@ -33,7 +32,7 @@ class FlavorSwapController(wsgi.Controller):
|
||||||
if not authorize(req.environ['nova.context']):
|
if not authorize(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
if 'flavor' in resp_obj.obj:
|
if 'flavor' in resp_obj.obj:
|
||||||
resp_obj.attach(xml=FlavorSwapTemplate())
|
resp_obj.attach()
|
||||||
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
|
@ -48,7 +47,7 @@ class FlavorSwapController(wsgi.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
if not authorize(req.environ['nova.context']):
|
if not authorize(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
resp_obj.attach(xml=FlavorsSwapTemplate())
|
resp_obj.attach()
|
||||||
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,23 +64,3 @@ class Flavor_swap(extensions.ExtensionDescriptor):
|
||||||
controller = FlavorSwapController()
|
controller = FlavorSwapController()
|
||||||
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_flavor(elem):
|
|
||||||
# NOTE(vish): this was originally added without a namespace
|
|
||||||
elem.set('swap', xmlutil.EmptyStringSelector('swap'))
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorSwapTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavor', selector='flavor')
|
|
||||||
make_flavor(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorsSwapTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'flavor', selector='flavors')
|
|
||||||
make_flavor(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ attributes. This extension adds to that list:
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'flavorextradata')
|
authorize = extensions.soft_extension_authorizer('compute', 'flavorextradata')
|
||||||
|
@ -41,7 +40,7 @@ class FlavorextradataController(wsgi.Controller):
|
||||||
if not authorize(req.environ['nova.context']):
|
if not authorize(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
if 'flavor' in resp_obj.obj:
|
if 'flavor' in resp_obj.obj:
|
||||||
resp_obj.attach(xml=FlavorextradatumTemplate())
|
resp_obj.attach()
|
||||||
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
self._extend_flavors(req, [resp_obj.obj['flavor']])
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
|
@ -56,7 +55,7 @@ class FlavorextradataController(wsgi.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
if not authorize(req.environ['nova.context']):
|
if not authorize(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
resp_obj.attach(xml=FlavorextradataTemplate())
|
resp_obj.attach()
|
||||||
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
self._extend_flavors(req, list(resp_obj.obj['flavors']))
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,27 +72,3 @@ class Flavorextradata(extensions.ExtensionDescriptor):
|
||||||
controller = FlavorextradataController()
|
controller = FlavorextradataController()
|
||||||
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
extension = extensions.ControllerExtension(self, 'flavors', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_flavor(elem):
|
|
||||||
elem.set('{%s}ephemeral' % Flavorextradata.namespace,
|
|
||||||
'%s:ephemeral' % Flavorextradata.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorextradatumTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavor', selector='flavor')
|
|
||||||
make_flavor(root)
|
|
||||||
alias = Flavorextradata.alias
|
|
||||||
namespace = Flavorextradata.namespace
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorextradataTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'flavor', selector='flavors')
|
|
||||||
make_flavor(elem)
|
|
||||||
alias = Flavorextradata.alias
|
|
||||||
namespace = Flavorextradata.namespace
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
|
||||||
|
|
|
@ -19,8 +19,6 @@ import six
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.compute import flavors
|
from nova.compute import flavors
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -30,21 +28,6 @@ from nova import utils
|
||||||
authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
|
authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
|
||||||
|
|
||||||
|
|
||||||
class ExtraSpecsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
extra_specs_dict = xmlutil.make_flat_dict('extra_specs', colon_ns=True)
|
|
||||||
return xmlutil.MasterTemplate(extra_specs_dict, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtraSpecTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
sel = xmlutil.Selector(xmlutil.get_items, 0)
|
|
||||||
root = xmlutil.TemplateElement('extra_spec', selector=sel)
|
|
||||||
root.set('key', 0)
|
|
||||||
root.text = 1
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorExtraSpecsController(object):
|
class FlavorExtraSpecsController(object):
|
||||||
"""The flavor extra specs API controller for the OpenStack API."""
|
"""The flavor extra specs API controller for the OpenStack API."""
|
||||||
|
|
||||||
|
@ -85,14 +68,12 @@ class FlavorExtraSpecsController(object):
|
||||||
except exception.InvalidInput as error:
|
except exception.InvalidInput as error:
|
||||||
raise exc.HTTPBadRequest(explanation=error.format_message())
|
raise exc.HTTPBadRequest(explanation=error.format_message())
|
||||||
|
|
||||||
@wsgi.serializers(xml=ExtraSpecsTemplate)
|
|
||||||
def index(self, req, flavor_id):
|
def index(self, req, flavor_id):
|
||||||
"""Returns the list of extra specs for a given flavor."""
|
"""Returns the list of extra specs for a given flavor."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action='index')
|
authorize(context, action='index')
|
||||||
return self._get_extra_specs(context, flavor_id)
|
return self._get_extra_specs(context, flavor_id)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ExtraSpecsTemplate)
|
|
||||||
def create(self, req, flavor_id, body):
|
def create(self, req, flavor_id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action='create')
|
authorize(context, action='create')
|
||||||
|
@ -109,7 +90,6 @@ class FlavorExtraSpecsController(object):
|
||||||
raise exc.HTTPNotFound(explanation=error.format_message())
|
raise exc.HTTPNotFound(explanation=error.format_message())
|
||||||
return body
|
return body
|
||||||
|
|
||||||
@wsgi.serializers(xml=ExtraSpecTemplate)
|
|
||||||
def update(self, req, flavor_id, id, body):
|
def update(self, req, flavor_id, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context, action='update')
|
authorize(context, action='update')
|
||||||
|
@ -130,7 +110,6 @@ class FlavorExtraSpecsController(object):
|
||||||
raise exc.HTTPNotFound(explanation=error.format_message())
|
raise exc.HTTPNotFound(explanation=error.format_message())
|
||||||
return body
|
return body
|
||||||
|
|
||||||
@wsgi.serializers(xml=ExtraSpecTemplate)
|
|
||||||
def show(self, req, flavor_id, id):
|
def show(self, req, flavor_id, id):
|
||||||
"""Return a single extra spec item."""
|
"""Return a single extra spec item."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute import flavors as flavors_api
|
|
||||||
from nova.api.openstack.compute.views import flavors as flavors_view
|
from nova.api.openstack.compute.views import flavors as flavors_view
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
|
@ -47,7 +46,6 @@ class FlavorManageController(wsgi.Controller):
|
||||||
return webob.Response(status_int=202)
|
return webob.Response(status_int=202)
|
||||||
|
|
||||||
@wsgi.action("create")
|
@wsgi.action("create")
|
||||||
@wsgi.serializers(xml=flavors_api.FlavorTemplate)
|
|
||||||
def _create(self, req, body):
|
def _create(self, req, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
|
|
@ -19,7 +19,6 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import network
|
from nova import network
|
||||||
|
@ -28,55 +27,6 @@ from nova import network
|
||||||
authorize = extensions.extension_authorizer('compute', 'floating_ip_dns')
|
authorize = extensions.extension_authorizer('compute', 'floating_ip_dns')
|
||||||
|
|
||||||
|
|
||||||
def make_dns_entry(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('ip')
|
|
||||||
elem.set('type')
|
|
||||||
elem.set('domain')
|
|
||||||
elem.set('name')
|
|
||||||
|
|
||||||
|
|
||||||
def make_domain_entry(elem):
|
|
||||||
elem.set('domain')
|
|
||||||
elem.set('scope')
|
|
||||||
elem.set('project')
|
|
||||||
elem.set('availability_zone')
|
|
||||||
|
|
||||||
|
|
||||||
class FloatingIPDNSTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('dns_entry',
|
|
||||||
selector='dns_entry')
|
|
||||||
make_dns_entry(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FloatingIPDNSsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('dns_entries')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'dns_entry',
|
|
||||||
selector='dns_entries')
|
|
||||||
make_dns_entry(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class DomainTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('domain_entry',
|
|
||||||
selector='domain_entry')
|
|
||||||
make_domain_entry(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class DomainsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('domain_entries')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'domain_entry',
|
|
||||||
selector='domain_entries')
|
|
||||||
make_domain_entry(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
def _translate_dns_entry_view(dns_entry):
|
def _translate_dns_entry_view(dns_entry):
|
||||||
result = {}
|
result = {}
|
||||||
result['ip'] = dns_entry.get('ip')
|
result['ip'] = dns_entry.get('ip')
|
||||||
|
@ -133,7 +83,6 @@ class FloatingIPDNSDomainController(object):
|
||||||
self.network_api = network.API()
|
self.network_api = network.API()
|
||||||
super(FloatingIPDNSDomainController, self).__init__()
|
super(FloatingIPDNSDomainController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=DomainsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return a list of available DNS domains."""
|
"""Return a list of available DNS domains."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -152,7 +101,6 @@ class FloatingIPDNSDomainController(object):
|
||||||
|
|
||||||
return _translate_domain_entries_view(domainlist)
|
return _translate_domain_entries_view(domainlist)
|
||||||
|
|
||||||
@wsgi.serializers(xml=DomainTemplate)
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
"""Add or modify domain entry."""
|
"""Add or modify domain entry."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -211,7 +159,6 @@ class FloatingIPDNSEntryController(object):
|
||||||
self.network_api = network.API()
|
self.network_api = network.API()
|
||||||
super(FloatingIPDNSEntryController, self).__init__()
|
super(FloatingIPDNSEntryController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=FloatingIPDNSTemplate)
|
|
||||||
def show(self, req, domain_id, id):
|
def show(self, req, domain_id, id):
|
||||||
"""Return the DNS entry that corresponds to domain_id and id."""
|
"""Return the DNS entry that corresponds to domain_id and id."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -242,13 +189,11 @@ class FloatingIPDNSEntryController(object):
|
||||||
entrylist = [_create_dns_entry(floating_ip, entry, domain)
|
entrylist = [_create_dns_entry(floating_ip, entry, domain)
|
||||||
for entry in entries]
|
for entry in entries]
|
||||||
dns_entries = _translate_dns_entries_view(entrylist)
|
dns_entries = _translate_dns_entries_view(entrylist)
|
||||||
return wsgi.ResponseObject(dns_entries,
|
return wsgi.ResponseObject(dns_entries)
|
||||||
xml=FloatingIPDNSsTemplate)
|
|
||||||
|
|
||||||
entry = _create_dns_entry(entries[0], id, domain)
|
entry = _create_dns_entry(entries[0], id, domain)
|
||||||
return _translate_dns_entry_view(entry)
|
return _translate_dns_entry_view(entry)
|
||||||
|
|
||||||
@wsgi.serializers(xml=FloatingIPDNSTemplate)
|
|
||||||
def update(self, req, domain_id, id, body):
|
def update(self, req, domain_id, id, body):
|
||||||
"""Add or modify dns entry."""
|
"""Add or modify dns entry."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import network
|
from nova import network
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,27 +32,6 @@ def _translate_floating_ip_pools_view(pools):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_float_ip(elem):
|
|
||||||
elem.set('name')
|
|
||||||
|
|
||||||
|
|
||||||
class FloatingIPPoolTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('floating_ip_pool',
|
|
||||||
selector='floating_ip_pool')
|
|
||||||
make_float_ip(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FloatingIPPoolsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('floating_ip_pools')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'floating_ip_pool',
|
|
||||||
selector='floating_ip_pools')
|
|
||||||
make_float_ip(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FloatingIPPoolsController(object):
|
class FloatingIPPoolsController(object):
|
||||||
"""The Floating IP Pool API controller for the OpenStack API."""
|
"""The Floating IP Pool API controller for the OpenStack API."""
|
||||||
|
|
||||||
|
@ -62,7 +39,6 @@ class FloatingIPPoolsController(object):
|
||||||
self.network_api = network.API()
|
self.network_api = network.API()
|
||||||
super(FloatingIPPoolsController, self).__init__()
|
super(FloatingIPPoolsController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=FloatingIPPoolsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return a list of pools."""
|
"""Return a list of pools."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -20,7 +20,6 @@ import webob
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova.compute import utils as compute_utils
|
from nova.compute import utils as compute_utils
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
@ -35,31 +34,6 @@ LOG = logging.getLogger(__name__)
|
||||||
authorize = extensions.extension_authorizer('compute', 'floating_ips')
|
authorize = extensions.extension_authorizer('compute', 'floating_ips')
|
||||||
|
|
||||||
|
|
||||||
def make_float_ip(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('ip')
|
|
||||||
elem.set('pool')
|
|
||||||
elem.set('fixed_ip')
|
|
||||||
elem.set('instance_id')
|
|
||||||
|
|
||||||
|
|
||||||
class FloatingIPTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('floating_ip',
|
|
||||||
selector='floating_ip')
|
|
||||||
make_float_ip(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FloatingIPsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('floating_ips')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'floating_ip',
|
|
||||||
selector='floating_ips')
|
|
||||||
make_float_ip(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
def _translate_floating_ip_view(floating_ip):
|
def _translate_floating_ip_view(floating_ip):
|
||||||
result = {
|
result = {
|
||||||
'id': floating_ip['id'],
|
'id': floating_ip['id'],
|
||||||
|
@ -108,7 +82,6 @@ class FloatingIPController(object):
|
||||||
self.network_api = network.API()
|
self.network_api = network.API()
|
||||||
super(FloatingIPController, self).__init__()
|
super(FloatingIPController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=FloatingIPTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return data about the given floating ip."""
|
"""Return data about the given floating ip."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -122,7 +95,6 @@ class FloatingIPController(object):
|
||||||
|
|
||||||
return _translate_floating_ip_view(floating_ip)
|
return _translate_floating_ip_view(floating_ip)
|
||||||
|
|
||||||
@wsgi.serializers(xml=FloatingIPsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return a list of floating ips allocated to a project."""
|
"""Return a list of floating ips allocated to a project."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -132,7 +104,6 @@ class FloatingIPController(object):
|
||||||
|
|
||||||
return _translate_floating_ips_view(floating_ips)
|
return _translate_floating_ips_view(floating_ips)
|
||||||
|
|
||||||
@wsgi.serializers(xml=FloatingIPTemplate)
|
|
||||||
def create(self, req, body=None):
|
def create(self, req, body=None):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
|
|
@ -18,8 +18,6 @@
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -29,72 +27,12 @@ LOG = logging.getLogger(__name__)
|
||||||
authorize = extensions.extension_authorizer('compute', 'hosts')
|
authorize = extensions.extension_authorizer('compute', 'hosts')
|
||||||
|
|
||||||
|
|
||||||
class HostIndexTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('hosts')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'host', selector='hosts')
|
|
||||||
elem.set('host_name')
|
|
||||||
elem.set('service')
|
|
||||||
elem.set('zone')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HostUpdateTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('host')
|
|
||||||
root.set('host')
|
|
||||||
root.set('status')
|
|
||||||
root.set('maintenance_mode')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HostActionTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('host')
|
|
||||||
root.set('host')
|
|
||||||
root.set('power_action')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HostShowTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('host')
|
|
||||||
elem = xmlutil.make_flat_dict('resource', selector='host',
|
|
||||||
subselector='resource')
|
|
||||||
root.append(elem)
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HostUpdateDeserializer(wsgi.XMLDeserializer):
|
|
||||||
def default(self, string):
|
|
||||||
node = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
|
|
||||||
updates = {}
|
|
||||||
updates_node = self.find_first_child_named(node, 'updates')
|
|
||||||
if updates_node is not None:
|
|
||||||
maintenance = self.find_first_child_named(updates_node,
|
|
||||||
'maintenance_mode')
|
|
||||||
if maintenance is not None:
|
|
||||||
updates[maintenance.tagName] = self.extract_text(maintenance)
|
|
||||||
|
|
||||||
status = self.find_first_child_named(updates_node, 'status')
|
|
||||||
if status is not None:
|
|
||||||
updates[status.tagName] = self.extract_text(status)
|
|
||||||
|
|
||||||
return dict(body=updates)
|
|
||||||
|
|
||||||
|
|
||||||
class HostController(object):
|
class HostController(object):
|
||||||
"""The Hosts API controller for the OpenStack API."""
|
"""The Hosts API controller for the OpenStack API."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.api = compute.HostAPI()
|
self.api = compute.HostAPI()
|
||||||
super(HostController, self).__init__()
|
super(HostController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=HostIndexTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Returns a dict in the format:
|
"""Returns a dict in the format:
|
||||||
|
|
||||||
|
@ -148,8 +86,6 @@ class HostController(object):
|
||||||
'zone': service['availability_zone']})
|
'zone': service['availability_zone']})
|
||||||
return {'hosts': hosts}
|
return {'hosts': hosts}
|
||||||
|
|
||||||
@wsgi.serializers(xml=HostUpdateTemplate)
|
|
||||||
@wsgi.deserializers(xml=HostUpdateDeserializer)
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
"""Updates a specified body.
|
"""Updates a specified body.
|
||||||
|
|
||||||
|
@ -261,15 +197,12 @@ class HostController(object):
|
||||||
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
|
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
|
||||||
return {"host": host_name, "power_action": result}
|
return {"host": host_name, "power_action": result}
|
||||||
|
|
||||||
@wsgi.serializers(xml=HostActionTemplate)
|
|
||||||
def startup(self, req, id):
|
def startup(self, req, id):
|
||||||
return self._host_power_action(req, host_name=id, action="startup")
|
return self._host_power_action(req, host_name=id, action="startup")
|
||||||
|
|
||||||
@wsgi.serializers(xml=HostActionTemplate)
|
|
||||||
def shutdown(self, req, id):
|
def shutdown(self, req, id):
|
||||||
return self._host_power_action(req, host_name=id, action="shutdown")
|
return self._host_power_action(req, host_name=id, action="shutdown")
|
||||||
|
|
||||||
@wsgi.serializers(xml=HostActionTemplate)
|
|
||||||
def reboot(self, req, id):
|
def reboot(self, req, id):
|
||||||
return self._host_power_action(req, host_name=id, action="reboot")
|
return self._host_power_action(req, host_name=id, action="reboot")
|
||||||
|
|
||||||
|
@ -322,7 +255,6 @@ class HostController(object):
|
||||||
instance['ephemeral_gb'])
|
instance['ephemeral_gb'])
|
||||||
return project_map
|
return project_map
|
||||||
|
|
||||||
@wsgi.serializers(xml=HostShowTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Shows the physical/usage resource given by hosts.
|
"""Shows the physical/usage resource given by hosts.
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,6 @@
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -29,104 +27,6 @@ from nova import servicegroup
|
||||||
authorize = extensions.extension_authorizer('compute', 'hypervisors')
|
authorize = extensions.extension_authorizer('compute', 'hypervisors')
|
||||||
|
|
||||||
|
|
||||||
def make_hypervisor(elem, detail):
|
|
||||||
elem.set('hypervisor_hostname')
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('state')
|
|
||||||
elem.set('status')
|
|
||||||
if detail:
|
|
||||||
elem.set('vcpus')
|
|
||||||
elem.set('memory_mb')
|
|
||||||
elem.set('local_gb')
|
|
||||||
elem.set('vcpus_used')
|
|
||||||
elem.set('memory_mb_used')
|
|
||||||
elem.set('local_gb_used')
|
|
||||||
elem.set('hypervisor_type')
|
|
||||||
elem.set('hypervisor_version')
|
|
||||||
elem.set('free_ram_mb')
|
|
||||||
elem.set('free_disk_gb')
|
|
||||||
elem.set('current_workload')
|
|
||||||
elem.set('running_vms')
|
|
||||||
elem.set('cpu_info')
|
|
||||||
elem.set('disk_available_least')
|
|
||||||
elem.set('host_ip')
|
|
||||||
|
|
||||||
service = xmlutil.SubTemplateElement(elem, 'service',
|
|
||||||
selector='service')
|
|
||||||
service.set('id')
|
|
||||||
service.set('host')
|
|
||||||
service.set('disabled_reason')
|
|
||||||
|
|
||||||
|
|
||||||
class HypervisorIndexTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('hypervisors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'hypervisor',
|
|
||||||
selector='hypervisors')
|
|
||||||
make_hypervisor(elem, False)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HypervisorDetailTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('hypervisors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'hypervisor',
|
|
||||||
selector='hypervisors')
|
|
||||||
make_hypervisor(elem, True)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HypervisorTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('hypervisor', selector='hypervisor')
|
|
||||||
make_hypervisor(root, True)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HypervisorUptimeTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('hypervisor', selector='hypervisor')
|
|
||||||
make_hypervisor(root, False)
|
|
||||||
root.set('uptime')
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HypervisorServersTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('hypervisors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'hypervisor',
|
|
||||||
selector='hypervisors')
|
|
||||||
make_hypervisor(elem, False)
|
|
||||||
|
|
||||||
servers = xmlutil.SubTemplateElement(elem, 'servers')
|
|
||||||
server = xmlutil.SubTemplateElement(servers, 'server',
|
|
||||||
selector='servers')
|
|
||||||
server.set('name')
|
|
||||||
server.set('uuid')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HypervisorStatisticsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('hypervisor_statistics',
|
|
||||||
selector='hypervisor_statistics')
|
|
||||||
root.set('count')
|
|
||||||
root.set('vcpus')
|
|
||||||
root.set('memory_mb')
|
|
||||||
root.set('local_gb')
|
|
||||||
root.set('vcpus_used')
|
|
||||||
root.set('memory_mb_used')
|
|
||||||
root.set('local_gb_used')
|
|
||||||
root.set('free_ram_mb')
|
|
||||||
root.set('free_disk_gb')
|
|
||||||
root.set('current_workload')
|
|
||||||
root.set('running_vms')
|
|
||||||
root.set('disk_available_least')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class HypervisorsController(object):
|
class HypervisorsController(object):
|
||||||
"""The Hypervisors API controller for the OpenStack API."""
|
"""The Hypervisors API controller for the OpenStack API."""
|
||||||
|
|
||||||
|
@ -179,7 +79,6 @@ class HypervisorsController(object):
|
||||||
|
|
||||||
return hyp_dict
|
return hyp_dict
|
||||||
|
|
||||||
@wsgi.serializers(xml=HypervisorIndexTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -188,7 +87,6 @@ class HypervisorsController(object):
|
||||||
return dict(hypervisors=[self._view_hypervisor(hyp, False)
|
return dict(hypervisors=[self._view_hypervisor(hyp, False)
|
||||||
for hyp in compute_nodes])
|
for hyp in compute_nodes])
|
||||||
|
|
||||||
@wsgi.serializers(xml=HypervisorDetailTemplate)
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -197,7 +95,6 @@ class HypervisorsController(object):
|
||||||
return dict(hypervisors=[self._view_hypervisor(hyp, True)
|
return dict(hypervisors=[self._view_hypervisor(hyp, True)
|
||||||
for hyp in compute_nodes])
|
for hyp in compute_nodes])
|
||||||
|
|
||||||
@wsgi.serializers(xml=HypervisorTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -209,7 +106,6 @@ class HypervisorsController(object):
|
||||||
raise webob.exc.HTTPNotFound(explanation=msg)
|
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||||
return dict(hypervisor=self._view_hypervisor(hyp, True))
|
return dict(hypervisor=self._view_hypervisor(hyp, True))
|
||||||
|
|
||||||
@wsgi.serializers(xml=HypervisorUptimeTemplate)
|
|
||||||
def uptime(self, req, id):
|
def uptime(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -231,7 +127,6 @@ class HypervisorsController(object):
|
||||||
return dict(hypervisor=self._view_hypervisor(hyp, False,
|
return dict(hypervisor=self._view_hypervisor(hyp, False,
|
||||||
uptime=uptime))
|
uptime=uptime))
|
||||||
|
|
||||||
@wsgi.serializers(xml=HypervisorIndexTemplate)
|
|
||||||
def search(self, req, id):
|
def search(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -244,7 +139,6 @@ class HypervisorsController(object):
|
||||||
msg = _("No hypervisor matching '%s' could be found.") % id
|
msg = _("No hypervisor matching '%s' could be found.") % id
|
||||||
raise webob.exc.HTTPNotFound(explanation=msg)
|
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||||
|
|
||||||
@wsgi.serializers(xml=HypervisorServersTemplate)
|
|
||||||
def servers(self, req, id):
|
def servers(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -261,7 +155,6 @@ class HypervisorsController(object):
|
||||||
hypervisors.append(hyp)
|
hypervisors.append(hyp)
|
||||||
return dict(hypervisors=hypervisors)
|
return dict(hypervisors=hypervisors)
|
||||||
|
|
||||||
@wsgi.serializers(xml=HypervisorStatisticsTemplate)
|
|
||||||
def statistics(self, req):
|
def statistics(self, req):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
|
|
@ -15,32 +15,10 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
authorize = extensions.soft_extension_authorizer('compute', 'image_size')
|
authorize = extensions.soft_extension_authorizer('compute', 'image_size')
|
||||||
|
|
||||||
|
|
||||||
def make_image(elem):
|
|
||||||
elem.set('{%s}size' % Image_size.namespace, '%s:size' % Image_size.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class ImagesSizeTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('images')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'image', selector='images')
|
|
||||||
make_image(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Image_size.alias: Image_size.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ImageSizeTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('image', selector='image')
|
|
||||||
make_image(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Image_size.alias: Image_size.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ImageSizeController(wsgi.Controller):
|
class ImageSizeController(wsgi.Controller):
|
||||||
|
|
||||||
def _extend_image(self, image, image_cache):
|
def _extend_image(self, image, image_cache):
|
||||||
|
@ -52,7 +30,7 @@ class ImageSizeController(wsgi.Controller):
|
||||||
context = req.environ["nova.context"]
|
context = req.environ["nova.context"]
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ImageSizeTemplate())
|
resp_obj.attach()
|
||||||
image_resp = resp_obj.obj['image']
|
image_resp = resp_obj.obj['image']
|
||||||
# image guaranteed to be in the cache due to the core API adding
|
# image guaranteed to be in the cache due to the core API adding
|
||||||
# it in its 'show' method
|
# it in its 'show' method
|
||||||
|
@ -64,7 +42,7 @@ class ImageSizeController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ImagesSizeTemplate())
|
resp_obj.attach()
|
||||||
images_resp = list(resp_obj.obj['images'])
|
images_resp = list(resp_obj.obj['images'])
|
||||||
# images guaranteed to be in the cache due to the core API adding
|
# images guaranteed to be in the cache due to the core API adding
|
||||||
# it in its 'detail' method
|
# it in its 'detail' method
|
||||||
|
|
|
@ -18,7 +18,6 @@ from webob import exc
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
|
|
||||||
authorize_actions = extensions.extension_authorizer('compute',
|
authorize_actions = extensions.extension_authorizer('compute',
|
||||||
|
@ -31,37 +30,6 @@ ACTION_KEYS = ['action', 'instance_uuid', 'request_id', 'user_id',
|
||||||
EVENT_KEYS = ['event', 'start_time', 'finish_time', 'result', 'traceback']
|
EVENT_KEYS = ['event', 'start_time', 'finish_time', 'result', 'traceback']
|
||||||
|
|
||||||
|
|
||||||
def make_actions(elem):
|
|
||||||
for key in ACTION_KEYS:
|
|
||||||
elem.set(key)
|
|
||||||
|
|
||||||
|
|
||||||
def make_action(elem):
|
|
||||||
for key in ACTION_KEYS:
|
|
||||||
elem.set(key)
|
|
||||||
event = xmlutil.TemplateElement('events', selector='events')
|
|
||||||
for key in EVENT_KEYS:
|
|
||||||
event.set(key)
|
|
||||||
elem.append(event)
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceActionsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('instanceActions')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'instanceAction',
|
|
||||||
selector='instanceActions')
|
|
||||||
make_actions(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceActionTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('instanceAction',
|
|
||||||
selector='instanceAction')
|
|
||||||
make_action(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceActionsController(wsgi.Controller):
|
class InstanceActionsController(wsgi.Controller):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -81,7 +49,6 @@ class InstanceActionsController(wsgi.Controller):
|
||||||
event[key] = event_raw.get(key)
|
event[key] = event_raw.get(key)
|
||||||
return event
|
return event
|
||||||
|
|
||||||
@wsgi.serializers(xml=InstanceActionsTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
"""Returns the list of actions recorded for a given instance."""
|
"""Returns the list of actions recorded for a given instance."""
|
||||||
context = req.environ["nova.context"]
|
context = req.environ["nova.context"]
|
||||||
|
@ -91,7 +58,6 @@ class InstanceActionsController(wsgi.Controller):
|
||||||
actions = [self._format_action(action) for action in actions_raw]
|
actions = [self._format_action(action) for action in actions_raw]
|
||||||
return {'instanceActions': actions}
|
return {'instanceActions': actions}
|
||||||
|
|
||||||
@wsgi.serializers(xml=InstanceActionTemplate)
|
|
||||||
def show(self, req, server_id, id):
|
def show(self, req, server_id, id):
|
||||||
"""Return data about the given instance action."""
|
"""Return data about the given instance action."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -21,7 +21,6 @@ import webob.exc
|
||||||
from nova.api.openstack.compute import servers
|
from nova.api.openstack.compute import servers
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.compute import api as compute_api
|
from nova.compute import api as compute_api
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -31,21 +30,6 @@ authorize = extensions.extension_authorizer('compute', 'keypairs')
|
||||||
soft_authorize = extensions.soft_extension_authorizer('compute', 'keypairs')
|
soft_authorize = extensions.soft_extension_authorizer('compute', 'keypairs')
|
||||||
|
|
||||||
|
|
||||||
class KeypairTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
return xmlutil.MasterTemplate(xmlutil.make_flat_dict('keypair'), 1)
|
|
||||||
|
|
||||||
|
|
||||||
class KeypairsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('keypairs')
|
|
||||||
elem = xmlutil.make_flat_dict('keypair', selector='keypairs',
|
|
||||||
subselector='keypair')
|
|
||||||
root.append(elem)
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class KeypairController(object):
|
class KeypairController(object):
|
||||||
|
|
||||||
"""Keypair API controller for the OpenStack API."""
|
"""Keypair API controller for the OpenStack API."""
|
||||||
|
@ -62,7 +46,6 @@ class KeypairController(object):
|
||||||
clean[attr] = keypair[attr]
|
clean[attr] = keypair[attr]
|
||||||
return clean
|
return clean
|
||||||
|
|
||||||
@wsgi.serializers(xml=KeypairTemplate)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Create or import keypair.
|
"""Create or import keypair.
|
||||||
|
|
||||||
|
@ -118,7 +101,6 @@ class KeypairController(object):
|
||||||
raise webob.exc.HTTPNotFound(explanation=exc.format_message())
|
raise webob.exc.HTTPNotFound(explanation=exc.format_message())
|
||||||
return webob.Response(status_int=202)
|
return webob.Response(status_int=202)
|
||||||
|
|
||||||
@wsgi.serializers(xml=KeypairTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return data for the given key name."""
|
"""Return data for the given key name."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -130,7 +112,6 @@ class KeypairController(object):
|
||||||
raise webob.exc.HTTPNotFound(explanation=exc.format_message())
|
raise webob.exc.HTTPNotFound(explanation=exc.format_message())
|
||||||
return {'keypair': keypair}
|
return {'keypair': keypair}
|
||||||
|
|
||||||
@wsgi.serializers(xml=KeypairsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""List of keypairs for a user."""
|
"""List of keypairs for a user."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -143,21 +124,6 @@ class KeypairController(object):
|
||||||
return {'keypairs': rval}
|
return {'keypairs': rval}
|
||||||
|
|
||||||
|
|
||||||
class ServerKeyNameTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server')
|
|
||||||
root.set('key_name', 'key_name')
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ServersKeyNameTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
elem.set('key_name', 'key_name')
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(servers.Controller):
|
class Controller(servers.Controller):
|
||||||
|
|
||||||
def _add_key_name(self, req, servers):
|
def _add_key_name(self, req, servers):
|
||||||
|
@ -169,7 +135,7 @@ class Controller(servers.Controller):
|
||||||
|
|
||||||
def _show(self, req, resp_obj):
|
def _show(self, req, resp_obj):
|
||||||
if 'server' in resp_obj.obj:
|
if 'server' in resp_obj.obj:
|
||||||
resp_obj.attach(xml=ServerKeyNameTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
self._add_key_name(req, [server])
|
self._add_key_name(req, [server])
|
||||||
|
|
||||||
|
@ -183,7 +149,7 @@ class Controller(servers.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if 'servers' in resp_obj.obj and soft_authorize(context):
|
if 'servers' in resp_obj.obj and soft_authorize(context):
|
||||||
resp_obj.attach(xml=ServersKeyNameTemplate())
|
resp_obj.attach()
|
||||||
servers = resp_obj.obj['servers']
|
servers = resp_obj.obj['servers']
|
||||||
self._add_key_name(req, servers)
|
self._add_key_name(req, servers)
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova.objects import base as obj_base
|
from nova.objects import base as obj_base
|
||||||
|
|
||||||
|
@ -39,33 +37,11 @@ def output(migrations_obj):
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
|
|
||||||
class MigrationsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('migrations')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'migration',
|
|
||||||
selector='migrations')
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('source_node')
|
|
||||||
elem.set('dest_node')
|
|
||||||
elem.set('source_compute')
|
|
||||||
elem.set('dest_compute')
|
|
||||||
elem.set('dest_host')
|
|
||||||
elem.set('status')
|
|
||||||
elem.set('instance_uuid')
|
|
||||||
elem.set('old_instance_type_id')
|
|
||||||
elem.set('new_instance_type_id')
|
|
||||||
elem.set('created_at')
|
|
||||||
elem.set('updated_at')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class MigrationsController(object):
|
class MigrationsController(object):
|
||||||
"""Controller for accessing migrations in OpenStack API."""
|
"""Controller for accessing migrations in OpenStack API."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.compute_api = compute.API()
|
self.compute_api = compute.API()
|
||||||
|
|
||||||
@wsgi.serializers(xml=MigrationsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return all migrations in progress."""
|
"""Return all migrations in progress."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -17,7 +17,6 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
import nova.context
|
import nova.context
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
@ -35,20 +34,6 @@ EXTENDED_QUOTAS = {'server_groups': 'os-server-group-quotas',
|
||||||
authorize = extensions.extension_authorizer('compute', 'quota_classes')
|
authorize = extensions.extension_authorizer('compute', 'quota_classes')
|
||||||
|
|
||||||
|
|
||||||
class QuotaClassTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('quota_class_set',
|
|
||||||
selector='quota_class_set')
|
|
||||||
root.set('id')
|
|
||||||
|
|
||||||
for resource in QUOTAS.resources:
|
|
||||||
if resource not in EXTENDED_QUOTAS:
|
|
||||||
elem = xmlutil.SubTemplateElement(root, resource)
|
|
||||||
elem.text = resource
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaClassSetsController(wsgi.Controller):
|
class QuotaClassSetsController(wsgi.Controller):
|
||||||
|
|
||||||
supported_quotas = []
|
supported_quotas = []
|
||||||
|
@ -74,7 +59,6 @@ class QuotaClassSetsController(wsgi.Controller):
|
||||||
|
|
||||||
return dict(quota_class_set=result)
|
return dict(quota_class_set=result)
|
||||||
|
|
||||||
@wsgi.serializers(xml=QuotaClassTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -85,7 +69,6 @@ class QuotaClassSetsController(wsgi.Controller):
|
||||||
except exception.Forbidden:
|
except exception.Forbidden:
|
||||||
raise webob.exc.HTTPForbidden()
|
raise webob.exc.HTTPForbidden()
|
||||||
|
|
||||||
@wsgi.serializers(xml=QuotaClassTemplate)
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
|
|
@ -19,7 +19,6 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
import nova.context
|
import nova.context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -40,19 +39,6 @@ authorize_show = extensions.extension_authorizer('compute', 'quotas:show')
|
||||||
authorize_delete = extensions.extension_authorizer('compute', 'quotas:delete')
|
authorize_delete = extensions.extension_authorizer('compute', 'quotas:delete')
|
||||||
|
|
||||||
|
|
||||||
class QuotaTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('quota_set', selector='quota_set')
|
|
||||||
root.set('id')
|
|
||||||
|
|
||||||
for resource in QUOTAS.resources:
|
|
||||||
if resource not in EXTENDED_QUOTAS:
|
|
||||||
elem = xmlutil.SubTemplateElement(root, resource)
|
|
||||||
elem.text = resource
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class QuotaSetsController(wsgi.Controller):
|
class QuotaSetsController(wsgi.Controller):
|
||||||
|
|
||||||
supported_quotas = []
|
supported_quotas = []
|
||||||
|
@ -113,7 +99,6 @@ class QuotaSetsController(wsgi.Controller):
|
||||||
else:
|
else:
|
||||||
return dict((k, v['limit']) for k, v in values.items())
|
return dict((k, v['limit']) for k, v in values.items())
|
||||||
|
|
||||||
@wsgi.serializers(xml=QuotaTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize_show(context)
|
authorize_show(context)
|
||||||
|
@ -128,7 +113,6 @@ class QuotaSetsController(wsgi.Controller):
|
||||||
except exception.Forbidden:
|
except exception.Forbidden:
|
||||||
raise webob.exc.HTTPForbidden()
|
raise webob.exc.HTTPForbidden()
|
||||||
|
|
||||||
@wsgi.serializers(xml=QuotaTemplate)
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize_update(context)
|
authorize_update(context)
|
||||||
|
@ -206,7 +190,6 @@ class QuotaSetsController(wsgi.Controller):
|
||||||
values = self._get_quotas(context, id, user_id=user_id)
|
values = self._get_quotas(context, id, user_id=user_id)
|
||||||
return self._format_quota_set(None, values)
|
return self._format_quota_set(None, values)
|
||||||
|
|
||||||
@wsgi.serializers(xml=QuotaTemplate)
|
|
||||||
def defaults(self, req, id):
|
def defaults(self, req, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize_show(context)
|
authorize_show(context)
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
from xml.dom import minidom
|
|
||||||
|
|
||||||
import six
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
@ -20,7 +19,6 @@ from webob import exc
|
||||||
from nova.api.openstack.compute.contrib import security_groups as sg
|
from nova.api.openstack.compute.contrib import security_groups as sg
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.network.security_group import openstack_driver
|
from nova.network.security_group import openstack_driver
|
||||||
|
@ -32,82 +30,12 @@ authorize = extensions.extension_authorizer('compute',
|
||||||
sg_nsmap = {None: wsgi.XMLNS_V11}
|
sg_nsmap = {None: wsgi.XMLNS_V11}
|
||||||
|
|
||||||
|
|
||||||
def make_default_rule(elem):
|
|
||||||
elem.set('id')
|
|
||||||
|
|
||||||
proto = xmlutil.SubTemplateElement(elem, 'ip_protocol')
|
|
||||||
proto.text = 'ip_protocol'
|
|
||||||
|
|
||||||
from_port = xmlutil.SubTemplateElement(elem, 'from_port')
|
|
||||||
from_port.text = 'from_port'
|
|
||||||
|
|
||||||
to_port = xmlutil.SubTemplateElement(elem, 'to_port')
|
|
||||||
to_port.text = 'to_port'
|
|
||||||
|
|
||||||
ip_range = xmlutil.SubTemplateElement(elem, 'ip_range',
|
|
||||||
selector='ip_range')
|
|
||||||
cidr = xmlutil.SubTemplateElement(ip_range, 'cidr')
|
|
||||||
cidr.text = 'cidr'
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupDefaultRulesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('security_group_default_rules')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'security_group_default_rule',
|
|
||||||
selector='security_group_default_rules')
|
|
||||||
|
|
||||||
make_default_rule(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupDefaultRuleTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('security_group_default_rule',
|
|
||||||
selector='security_group_default_rule')
|
|
||||||
make_default_rule(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupDefaultRulesXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
def default(self, string):
|
|
||||||
dom = minidom.parseString(string)
|
|
||||||
security_group_rule = self._extract_security_group_default_rule(dom)
|
|
||||||
return {'body': {'security_group_default_rule': security_group_rule}}
|
|
||||||
|
|
||||||
def _extract_security_group_default_rule(self, node):
|
|
||||||
sg_rule = {}
|
|
||||||
sg_rule_node = self.find_first_child_named(node,
|
|
||||||
'security_group_default_rule')
|
|
||||||
if sg_rule_node is not None:
|
|
||||||
ip_protocol_node = self.find_first_child_named(sg_rule_node,
|
|
||||||
"ip_protocol")
|
|
||||||
if ip_protocol_node is not None:
|
|
||||||
sg_rule['ip_protocol'] = self.extract_text(ip_protocol_node)
|
|
||||||
|
|
||||||
from_port_node = self.find_first_child_named(sg_rule_node,
|
|
||||||
"from_port")
|
|
||||||
if from_port_node is not None:
|
|
||||||
sg_rule['from_port'] = self.extract_text(from_port_node)
|
|
||||||
|
|
||||||
to_port_node = self.find_first_child_named(sg_rule_node, "to_port")
|
|
||||||
if to_port_node is not None:
|
|
||||||
sg_rule['to_port'] = self.extract_text(to_port_node)
|
|
||||||
|
|
||||||
cidr_node = self.find_first_child_named(sg_rule_node, "cidr")
|
|
||||||
if cidr_node is not None:
|
|
||||||
sg_rule['cidr'] = self.extract_text(cidr_node)
|
|
||||||
|
|
||||||
return sg_rule
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase):
|
class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.security_group_api = (
|
self.security_group_api = (
|
||||||
openstack_driver.get_openstack_security_group_driver())
|
openstack_driver.get_openstack_security_group_driver())
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupDefaultRuleTemplate)
|
|
||||||
@wsgi.deserializers(xml=SecurityGroupDefaultRulesXMLDeserializer)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
context = sg._authorize_context(req)
|
context = sg._authorize_context(req)
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -141,7 +69,6 @@ class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase):
|
||||||
return self.security_group_api.new_cidr_ingress_rule(
|
return self.security_group_api.new_cidr_ingress_rule(
|
||||||
cidr, ip_protocol, from_port, to_port)
|
cidr, ip_protocol, from_port, to_port)
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupDefaultRuleTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
context = sg._authorize_context(req)
|
context = sg._authorize_context(req)
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -174,7 +101,6 @@ class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase):
|
||||||
|
|
||||||
return webob.Response(status_int=204)
|
return webob.Response(status_int=204)
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupDefaultRulesTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
|
|
||||||
context = sg._authorize_context(req)
|
context = sg._authorize_context(req)
|
||||||
|
|
|
@ -27,7 +27,6 @@ from webob import exc
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -41,44 +40,6 @@ authorize = extensions.extension_authorizer('compute', 'security_groups')
|
||||||
softauth = extensions.soft_extension_authorizer('compute', 'security_groups')
|
softauth = extensions.soft_extension_authorizer('compute', 'security_groups')
|
||||||
|
|
||||||
|
|
||||||
def make_rule(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('parent_group_id')
|
|
||||||
|
|
||||||
proto = xmlutil.SubTemplateElement(elem, 'ip_protocol')
|
|
||||||
proto.text = 'ip_protocol'
|
|
||||||
|
|
||||||
from_port = xmlutil.SubTemplateElement(elem, 'from_port')
|
|
||||||
from_port.text = 'from_port'
|
|
||||||
|
|
||||||
to_port = xmlutil.SubTemplateElement(elem, 'to_port')
|
|
||||||
to_port.text = 'to_port'
|
|
||||||
|
|
||||||
group = xmlutil.SubTemplateElement(elem, 'group', selector='group')
|
|
||||||
name = xmlutil.SubTemplateElement(group, 'name')
|
|
||||||
name.text = 'name'
|
|
||||||
tenant_id = xmlutil.SubTemplateElement(group, 'tenant_id')
|
|
||||||
tenant_id.text = 'tenant_id'
|
|
||||||
|
|
||||||
ip_range = xmlutil.SubTemplateElement(elem, 'ip_range',
|
|
||||||
selector='ip_range')
|
|
||||||
cidr = xmlutil.SubTemplateElement(ip_range, 'cidr')
|
|
||||||
cidr.text = 'cidr'
|
|
||||||
|
|
||||||
|
|
||||||
def make_sg(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('tenant_id')
|
|
||||||
elem.set('name')
|
|
||||||
|
|
||||||
desc = xmlutil.SubTemplateElement(elem, 'description')
|
|
||||||
desc.text = 'description'
|
|
||||||
|
|
||||||
rules = xmlutil.SubTemplateElement(elem, 'rules')
|
|
||||||
rule = xmlutil.SubTemplateElement(rules, 'rule', selector='rules')
|
|
||||||
make_rule(rule)
|
|
||||||
|
|
||||||
|
|
||||||
def _authorize_context(req):
|
def _authorize_context(req):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
@ -87,96 +48,6 @@ def _authorize_context(req):
|
||||||
sg_nsmap = {None: wsgi.XMLNS_V11}
|
sg_nsmap = {None: wsgi.XMLNS_V11}
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupRuleTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('security_group_rule',
|
|
||||||
selector='security_group_rule')
|
|
||||||
make_rule(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('security_group',
|
|
||||||
selector='security_group')
|
|
||||||
make_sg(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('security_groups')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'security_group',
|
|
||||||
selector='security_groups')
|
|
||||||
make_sg(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
"""Deserializer to handle xml-formatted security group requests."""
|
|
||||||
def default(self, string):
|
|
||||||
"""Deserialize an xml-formatted security group create request."""
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
security_group = {}
|
|
||||||
sg_node = self.find_first_child_named(dom,
|
|
||||||
'security_group')
|
|
||||||
if sg_node is not None:
|
|
||||||
if sg_node.hasAttribute('name'):
|
|
||||||
security_group['name'] = sg_node.getAttribute('name')
|
|
||||||
desc_node = self.find_first_child_named(sg_node,
|
|
||||||
"description")
|
|
||||||
if desc_node:
|
|
||||||
security_group['description'] = self.extract_text(desc_node)
|
|
||||||
return {'body': {'security_group': security_group}}
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupRulesXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
"""Deserializer to handle xml-formatted security group requests."""
|
|
||||||
|
|
||||||
def default(self, string):
|
|
||||||
"""Deserialize an xml-formatted security group create request."""
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
security_group_rule = self._extract_security_group_rule(dom)
|
|
||||||
return {'body': {'security_group_rule': security_group_rule}}
|
|
||||||
|
|
||||||
def _extract_security_group_rule(self, node):
|
|
||||||
"""Marshal the security group rule attribute of a parsed request."""
|
|
||||||
sg_rule = {}
|
|
||||||
sg_rule_node = self.find_first_child_named(node,
|
|
||||||
'security_group_rule')
|
|
||||||
if sg_rule_node is not None:
|
|
||||||
ip_protocol_node = self.find_first_child_named(sg_rule_node,
|
|
||||||
"ip_protocol")
|
|
||||||
if ip_protocol_node is not None:
|
|
||||||
sg_rule['ip_protocol'] = self.extract_text(ip_protocol_node)
|
|
||||||
|
|
||||||
from_port_node = self.find_first_child_named(sg_rule_node,
|
|
||||||
"from_port")
|
|
||||||
if from_port_node is not None:
|
|
||||||
sg_rule['from_port'] = self.extract_text(from_port_node)
|
|
||||||
|
|
||||||
to_port_node = self.find_first_child_named(sg_rule_node, "to_port")
|
|
||||||
if to_port_node is not None:
|
|
||||||
sg_rule['to_port'] = self.extract_text(to_port_node)
|
|
||||||
|
|
||||||
parent_group_id_node = self.find_first_child_named(sg_rule_node,
|
|
||||||
"parent_group_id")
|
|
||||||
if parent_group_id_node is not None:
|
|
||||||
sg_rule['parent_group_id'] = self.extract_text(
|
|
||||||
parent_group_id_node)
|
|
||||||
|
|
||||||
group_id_node = self.find_first_child_named(sg_rule_node,
|
|
||||||
"group_id")
|
|
||||||
if group_id_node is not None:
|
|
||||||
sg_rule['group_id'] = self.extract_text(group_id_node)
|
|
||||||
|
|
||||||
cidr_node = self.find_first_child_named(sg_rule_node, "cidr")
|
|
||||||
if cidr_node is not None:
|
|
||||||
sg_rule['cidr'] = self.extract_text(cidr_node)
|
|
||||||
|
|
||||||
return sg_rule
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def translate_exceptions():
|
def translate_exceptions():
|
||||||
"""Translate nova exceptions to http exceptions."""
|
"""Translate nova exceptions to http exceptions."""
|
||||||
|
@ -273,7 +144,6 @@ class SecurityGroupControllerBase(object):
|
||||||
class SecurityGroupController(SecurityGroupControllerBase):
|
class SecurityGroupController(SecurityGroupControllerBase):
|
||||||
"""The Security group API controller for the OpenStack API."""
|
"""The Security group API controller for the OpenStack API."""
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return data about the given security group."""
|
"""Return data about the given security group."""
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
@ -298,7 +168,6 @@ class SecurityGroupController(SecurityGroupControllerBase):
|
||||||
|
|
||||||
return webob.Response(status_int=202)
|
return webob.Response(status_int=202)
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Returns a list of security groups."""
|
"""Returns a list of security groups."""
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
@ -320,8 +189,6 @@ class SecurityGroupController(SecurityGroupControllerBase):
|
||||||
list(sorted(result,
|
list(sorted(result,
|
||||||
key=lambda k: (k['tenant_id'], k['name'])))}
|
key=lambda k: (k['tenant_id'], k['name'])))}
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupTemplate)
|
|
||||||
@wsgi.deserializers(xml=SecurityGroupXMLDeserializer)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Creates a new security group."""
|
"""Creates a new security group."""
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
@ -341,7 +208,6 @@ class SecurityGroupController(SecurityGroupControllerBase):
|
||||||
return {'security_group': self._format_security_group(context,
|
return {'security_group': self._format_security_group(context,
|
||||||
group_ref)}
|
group_ref)}
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupTemplate)
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
"""Update a security group."""
|
"""Update a security group."""
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
@ -368,8 +234,6 @@ class SecurityGroupController(SecurityGroupControllerBase):
|
||||||
|
|
||||||
class SecurityGroupRulesController(SecurityGroupControllerBase):
|
class SecurityGroupRulesController(SecurityGroupControllerBase):
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupRuleTemplate)
|
|
||||||
@wsgi.deserializers(xml=SecurityGroupRulesXMLDeserializer)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
|
||||||
|
@ -455,7 +319,6 @@ class SecurityGroupRulesController(SecurityGroupControllerBase):
|
||||||
|
|
||||||
class ServerSecurityGroupController(SecurityGroupControllerBase):
|
class ServerSecurityGroupController(SecurityGroupControllerBase):
|
||||||
|
|
||||||
@wsgi.serializers(xml=SecurityGroupsTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
"""Returns a list of security groups for the given instance."""
|
"""Returns a list of security groups for the given instance."""
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
@ -592,7 +455,7 @@ class SecurityGroupsOutputController(wsgi.Controller):
|
||||||
if not softauth(req.environ['nova.context']):
|
if not softauth(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
if 'server' in resp_obj.obj:
|
if 'server' in resp_obj.obj:
|
||||||
resp_obj.attach(xml=SecurityGroupServerTemplate())
|
resp_obj.attach()
|
||||||
self._extend_servers(req, [resp_obj.obj['server']])
|
self._extend_servers(req, [resp_obj.obj['server']])
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
|
@ -607,38 +470,10 @@ class SecurityGroupsOutputController(wsgi.Controller):
|
||||||
def detail(self, req, resp_obj):
|
def detail(self, req, resp_obj):
|
||||||
if not softauth(req.environ['nova.context']):
|
if not softauth(req.environ['nova.context']):
|
||||||
return
|
return
|
||||||
resp_obj.attach(xml=SecurityGroupServersTemplate())
|
resp_obj.attach()
|
||||||
self._extend_servers(req, list(resp_obj.obj['servers']))
|
self._extend_servers(req, list(resp_obj.obj['servers']))
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupsTemplateElement(xmlutil.TemplateElement):
|
|
||||||
def will_render(self, datum):
|
|
||||||
return "security_groups" in datum
|
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem):
|
|
||||||
secgrps = SecurityGroupsTemplateElement('security_groups')
|
|
||||||
elem.append(secgrps)
|
|
||||||
secgrp = xmlutil.SubTemplateElement(secgrps, 'security_group',
|
|
||||||
selector="security_groups")
|
|
||||||
secgrp.set('name')
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupServerTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server')
|
|
||||||
make_server(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupServersTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class Security_groups(extensions.ExtensionDescriptor):
|
class Security_groups(extensions.ExtensionDescriptor):
|
||||||
"""Security group support."""
|
"""Security group support."""
|
||||||
name = "SecurityGroups"
|
name = "SecurityGroups"
|
||||||
|
|
|
@ -18,7 +18,6 @@ import webob.exc
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -28,20 +27,10 @@ authorize = extensions.extension_authorizer('compute', 'server_diagnostics')
|
||||||
sd_nsmap = {None: wsgi.XMLNS_V11}
|
sd_nsmap = {None: wsgi.XMLNS_V11}
|
||||||
|
|
||||||
|
|
||||||
class ServerDiagnosticsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('diagnostics')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, xmlutil.Selector(0),
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
elem.text = 1
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=sd_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerDiagnosticsController(object):
|
class ServerDiagnosticsController(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.compute_api = compute.API()
|
self.compute_api = compute.API()
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServerDiagnosticsTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
context = req.environ["nova.context"]
|
context = req.environ["nova.context"]
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
|
|
@ -16,7 +16,6 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -30,41 +29,12 @@ authorize = extensions.extension_authorizer('compute',
|
||||||
'os-server-external-events')
|
'os-server-external-events')
|
||||||
|
|
||||||
|
|
||||||
class EventTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('events')
|
|
||||||
elem1 = xmlutil.SubTemplateElement(root, 'event', selector='events')
|
|
||||||
elem2 = xmlutil.SubTemplateElement(elem1, xmlutil.Selector(0),
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
elem2.text = 1
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class EventDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
def _extract_event(self, event_node):
|
|
||||||
event = {}
|
|
||||||
for key in ('name', 'tag', 'server_uuid', 'status'):
|
|
||||||
node = self.find_first_child_named(event_node, key)
|
|
||||||
event[key] = self.extract_text(node)
|
|
||||||
return event
|
|
||||||
|
|
||||||
def default(self, string):
|
|
||||||
events = []
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
events_node = self.find_first_child_named(dom, 'events')
|
|
||||||
for event_node in self.find_children_named(events_node, 'event'):
|
|
||||||
events.append(self._extract_event(event_node))
|
|
||||||
return {'body': {'events': events}}
|
|
||||||
|
|
||||||
|
|
||||||
class ServerExternalEventsController(wsgi.Controller):
|
class ServerExternalEventsController(wsgi.Controller):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.compute_api = compute.API()
|
self.compute_api = compute.API()
|
||||||
super(ServerExternalEventsController, self).__init__()
|
super(ServerExternalEventsController, self).__init__()
|
||||||
|
|
||||||
@wsgi.deserializers(xml=EventDeserializer)
|
|
||||||
@wsgi.serializers(xml=EventTemplate)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Creates a new instance event."""
|
"""Creates a new instance event."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import quota
|
from nova import quota
|
||||||
|
|
||||||
QUOTAS = quota.QUOTAS
|
QUOTAS = quota.QUOTAS
|
||||||
|
@ -39,27 +38,17 @@ class ExtendedQuotaSetsController(wsgi.Controller):
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
def show(self, req, id, resp_obj):
|
def show(self, req, id, resp_obj):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedQuotaSetsTemplate())
|
resp_obj.attach()
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
def update(self, req, id, body, resp_obj):
|
def update(self, req, id, body, resp_obj):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedQuotaSetsTemplate())
|
resp_obj.attach()
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
def defaults(self, req, id, resp_obj):
|
def defaults(self, req, id, resp_obj):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedQuotaSetsTemplate())
|
resp_obj.attach()
|
||||||
|
|
||||||
|
|
||||||
class ExtendedQuotaSetsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('quota_set', selector='quota_set')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server_groups')
|
|
||||||
elem.text = 'server_groups'
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server_group_members')
|
|
||||||
elem.text = 'server_group_members'
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtendedQuotaClassSetsController(wsgi.Controller):
|
class ExtendedQuotaClassSetsController(wsgi.Controller):
|
||||||
|
@ -67,23 +56,12 @@ class ExtendedQuotaClassSetsController(wsgi.Controller):
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
def show(self, req, id, resp_obj):
|
def show(self, req, id, resp_obj):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedQuotaClassSetsTemplate())
|
resp_obj.attach()
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
def update(self, req, id, body, resp_obj):
|
def update(self, req, id, body, resp_obj):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ExtendedQuotaClassSetsTemplate())
|
resp_obj.attach()
|
||||||
|
|
||||||
|
|
||||||
class ExtendedQuotaClassSetsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('quota_class_set',
|
|
||||||
selector='quota_class_set')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server_groups')
|
|
||||||
elem.text = 'server_groups'
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server_group_members')
|
|
||||||
elem.text = 'server_group_members'
|
|
||||||
return xmlutil.SlaveTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class Server_group_quotas(extensions.ExtensionDescriptor):
|
class Server_group_quotas(extensions.ExtensionDescriptor):
|
||||||
|
|
|
@ -21,7 +21,6 @@ from webob import exc
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
import nova.exception
|
import nova.exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.i18n import _LE
|
from nova.i18n import _LE
|
||||||
|
@ -37,29 +36,7 @@ SUPPORTED_POLICIES = ['anti-affinity', 'affinity']
|
||||||
authorize = extensions.extension_authorizer('compute', 'server_groups')
|
authorize = extensions.extension_authorizer('compute', 'server_groups')
|
||||||
|
|
||||||
|
|
||||||
def make_policy(elem):
|
server_group_nsmap = {}
|
||||||
elem.text = str
|
|
||||||
|
|
||||||
|
|
||||||
def make_member(elem):
|
|
||||||
elem.text = str
|
|
||||||
|
|
||||||
|
|
||||||
def make_group(elem):
|
|
||||||
elem.set('name')
|
|
||||||
elem.set('id')
|
|
||||||
policies = xmlutil.SubTemplateElement(elem, 'policies')
|
|
||||||
policy = xmlutil.SubTemplateElement(policies, 'policy',
|
|
||||||
selector='policies')
|
|
||||||
make_policy(policy)
|
|
||||||
members = xmlutil.SubTemplateElement(elem, 'members')
|
|
||||||
member = xmlutil.SubTemplateElement(members, 'member',
|
|
||||||
selector='members')
|
|
||||||
make_member(member)
|
|
||||||
elem.append(common.MetadataTemplate())
|
|
||||||
|
|
||||||
|
|
||||||
server_group_nsmap = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
|
|
||||||
|
|
||||||
|
|
||||||
def _authorize_context(req):
|
def _authorize_context(req):
|
||||||
|
@ -68,65 +45,6 @@ def _authorize_context(req):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class ServerGroupTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server_group',
|
|
||||||
selector='server_group')
|
|
||||||
make_group(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=server_group_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerGroupsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server_groups')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server_group',
|
|
||||||
selector='server_groups')
|
|
||||||
# Note: listing server groups only shows name and uuid
|
|
||||||
make_group(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=server_group_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerGroupXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
"""Deserializer to handle xml-formatted server group requests."""
|
|
||||||
|
|
||||||
metadata_deserializer = common.MetadataXMLDeserializer()
|
|
||||||
|
|
||||||
def default(self, string):
|
|
||||||
"""Deserialize an xml-formatted server group create request."""
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
server_group = self._extract_server_group(dom)
|
|
||||||
return {'body': {'server_group': server_group}}
|
|
||||||
|
|
||||||
def _extract_server_group(self, node):
|
|
||||||
"""Marshal the instance attribute of a parsed request."""
|
|
||||||
server_group = {}
|
|
||||||
sg_node = self.find_first_child_named(node, 'server_group')
|
|
||||||
if sg_node is not None:
|
|
||||||
if sg_node.hasAttribute('name'):
|
|
||||||
server_group['name'] = sg_node.getAttribute('name')
|
|
||||||
|
|
||||||
if sg_node.hasAttribute('id'):
|
|
||||||
server_group['id'] = sg_node.getAttribute('id')
|
|
||||||
|
|
||||||
policies = self._extract_policies(sg_node)
|
|
||||||
server_group['policies'] = policies or []
|
|
||||||
|
|
||||||
return server_group
|
|
||||||
|
|
||||||
def _extract_policies(self, server_group_node):
|
|
||||||
"""Marshal the server group policies element of a parsed request."""
|
|
||||||
policies_node = self.find_first_child_named(server_group_node,
|
|
||||||
'policies')
|
|
||||||
if policies_node is not None:
|
|
||||||
policy_nodes = self.find_children_named(policies_node,
|
|
||||||
'policy')
|
|
||||||
policies = []
|
|
||||||
if policy_nodes is not None:
|
|
||||||
for node in policy_nodes:
|
|
||||||
policies.append(node.firstChild.nodeValue)
|
|
||||||
return policies
|
|
||||||
|
|
||||||
|
|
||||||
class ServerGroupController(wsgi.Controller):
|
class ServerGroupController(wsgi.Controller):
|
||||||
"""The Server group API controller for the OpenStack API."""
|
"""The Server group API controller for the OpenStack API."""
|
||||||
|
|
||||||
|
@ -210,7 +128,6 @@ class ServerGroupController(wsgi.Controller):
|
||||||
msg = _("unsupported fields: %s") % subbody.keys()
|
msg = _("unsupported fields: %s") % subbody.keys()
|
||||||
raise nova.exception.InvalidInput(reason=msg)
|
raise nova.exception.InvalidInput(reason=msg)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServerGroupTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return data about the given server group."""
|
"""Return data about the given server group."""
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
@ -255,7 +172,6 @@ class ServerGroupController(wsgi.Controller):
|
||||||
|
|
||||||
return webob.Response(status_int=204)
|
return webob.Response(status_int=204)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServerGroupsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Returns a list of server groups."""
|
"""Returns a list of server groups."""
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
@ -270,8 +186,6 @@ class ServerGroupController(wsgi.Controller):
|
||||||
for group in limited_list]
|
for group in limited_list]
|
||||||
return {'server_groups': result}
|
return {'server_groups': result}
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServerGroupTemplate)
|
|
||||||
@wsgi.deserializers(xml=ServerGroupXMLDeserializer)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Creates a new server group."""
|
"""Creates a new server group."""
|
||||||
context = _authorize_context(req)
|
context = _authorize_context(req)
|
||||||
|
@ -310,11 +224,6 @@ class ServerGroupController(wsgi.Controller):
|
||||||
return {'server_group': self._format_server_group(context, sg)}
|
return {'server_group': self._format_server_group(context, sg)}
|
||||||
|
|
||||||
|
|
||||||
class ServerGroupsTemplateElement(xmlutil.TemplateElement):
|
|
||||||
def will_render(self, datum):
|
|
||||||
return "server_groups" in datum
|
|
||||||
|
|
||||||
|
|
||||||
class Server_groups(extensions.ExtensionDescriptor):
|
class Server_groups(extensions.ExtensionDescriptor):
|
||||||
"""Server group support."""
|
"""Server group support."""
|
||||||
name = "ServerGroups"
|
name = "ServerGroups"
|
||||||
|
|
|
@ -19,26 +19,17 @@ from nova.api.metadata import password
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
|
|
||||||
|
|
||||||
authorize = extensions.extension_authorizer('compute', 'server_password')
|
authorize = extensions.extension_authorizer('compute', 'server_password')
|
||||||
|
|
||||||
|
|
||||||
class ServerPasswordTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('password', selector='password')
|
|
||||||
root.text = unicode
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerPasswordController(object):
|
class ServerPasswordController(object):
|
||||||
"""The Server Password API controller for the OpenStack API."""
|
"""The Server Password API controller for the OpenStack API."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.compute_api = compute.API()
|
self.compute_api = compute.API()
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServerPasswordTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
@ -42,7 +41,7 @@ class ServerUsageController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ServerUsageTemplate())
|
resp_obj.attach()
|
||||||
server = resp_obj.obj['server']
|
server = resp_obj.obj['server']
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
# server['id'] is guaranteed to be in the cache due to
|
# server['id'] is guaranteed to be in the cache due to
|
||||||
|
@ -54,7 +53,7 @@ class ServerUsageController(wsgi.Controller):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
if authorize(context):
|
if authorize(context):
|
||||||
# Attach our slave template to the response object
|
# Attach our slave template to the response object
|
||||||
resp_obj.attach(xml=ServerUsagesTemplate())
|
resp_obj.attach()
|
||||||
servers = list(resp_obj.obj['servers'])
|
servers = list(resp_obj.obj['servers'])
|
||||||
for server in servers:
|
for server in servers:
|
||||||
db_instance = req.get_db_instance(server['id'])
|
db_instance = req.get_db_instance(server['id'])
|
||||||
|
@ -76,27 +75,3 @@ class Server_usage(extensions.ExtensionDescriptor):
|
||||||
controller = ServerUsageController()
|
controller = ServerUsageController()
|
||||||
extension = extensions.ControllerExtension(self, 'servers', controller)
|
extension = extensions.ControllerExtension(self, 'servers', controller)
|
||||||
return [extension]
|
return [extension]
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem):
|
|
||||||
elem.set('{%s}launched_at' % Server_usage.namespace,
|
|
||||||
'%s:launched_at' % Server_usage.alias)
|
|
||||||
elem.set('{%s}terminated_at' % Server_usage.namespace,
|
|
||||||
'%s:terminated_at' % Server_usage.alias)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerUsageTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server', selector='server')
|
|
||||||
make_server(root)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Server_usage.alias: Server_usage.namespace})
|
|
||||||
|
|
||||||
|
|
||||||
class ServerUsagesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={
|
|
||||||
Server_usage.alias: Server_usage.namespace})
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ import webob.exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -26,48 +25,6 @@ from nova import utils
|
||||||
authorize = extensions.extension_authorizer('compute', 'services')
|
authorize = extensions.extension_authorizer('compute', 'services')
|
||||||
|
|
||||||
|
|
||||||
class ServicesIndexTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('services')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'service', selector='services')
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('binary')
|
|
||||||
elem.set('host')
|
|
||||||
elem.set('zone')
|
|
||||||
elem.set('status')
|
|
||||||
elem.set('state')
|
|
||||||
elem.set('updated_at')
|
|
||||||
elem.set('disabled_reason')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceUpdateTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('service', selector='service')
|
|
||||||
root.set('host')
|
|
||||||
root.set('binary')
|
|
||||||
root.set('status')
|
|
||||||
root.set('disabled_reason')
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceUpdateDeserializer(wsgi.XMLDeserializer):
|
|
||||||
def default(self, string):
|
|
||||||
node = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
service = {}
|
|
||||||
service_node = self.find_first_child_named(node, 'service')
|
|
||||||
if service_node is None:
|
|
||||||
return service
|
|
||||||
service['host'] = service_node.getAttribute('host')
|
|
||||||
service['binary'] = service_node.getAttribute('binary')
|
|
||||||
service['disabled_reason'] = service_node.getAttribute(
|
|
||||||
'disabled_reason')
|
|
||||||
|
|
||||||
return dict(body=service)
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceController(object):
|
class ServiceController(object):
|
||||||
|
|
||||||
def __init__(self, ext_mgr=None, *args, **kwargs):
|
def __init__(self, ext_mgr=None, *args, **kwargs):
|
||||||
|
@ -143,7 +100,6 @@ class ServiceController(object):
|
||||||
explanation = _("Service %s not found.") % id
|
explanation = _("Service %s not found.") % id
|
||||||
raise webob.exc.HTTPNotFound(explanation=explanation)
|
raise webob.exc.HTTPNotFound(explanation=explanation)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServicesIndexTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return a list of all running services."""
|
"""Return a list of all running services."""
|
||||||
detailed = self.ext_mgr.is_loaded('os-extended-services')
|
detailed = self.ext_mgr.is_loaded('os-extended-services')
|
||||||
|
@ -151,8 +107,6 @@ class ServiceController(object):
|
||||||
|
|
||||||
return {'services': services}
|
return {'services': services}
|
||||||
|
|
||||||
@wsgi.deserializers(xml=ServiceUpdateDeserializer)
|
|
||||||
@wsgi.serializers(xml=ServiceUpdateTemplate)
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
"""Enable/Disable scheduling for a service."""
|
"""Enable/Disable scheduling for a service."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -22,8 +22,6 @@ import six.moves.urllib.parse as urlparse
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import objects
|
from nova import objects
|
||||||
|
@ -34,23 +32,6 @@ authorize_list = extensions.extension_authorizer('compute',
|
||||||
'simple_tenant_usage:list')
|
'simple_tenant_usage:list')
|
||||||
|
|
||||||
|
|
||||||
def make_usage(elem):
|
|
||||||
for subelem_tag in ('tenant_id', 'total_local_gb_usage',
|
|
||||||
'total_vcpus_usage', 'total_memory_mb_usage',
|
|
||||||
'total_hours', 'start', 'stop'):
|
|
||||||
subelem = xmlutil.SubTemplateElement(elem, subelem_tag)
|
|
||||||
subelem.text = subelem_tag
|
|
||||||
|
|
||||||
server_usages = xmlutil.SubTemplateElement(elem, 'server_usages')
|
|
||||||
server_usage = xmlutil.SubTemplateElement(server_usages, 'server_usage',
|
|
||||||
selector='server_usages')
|
|
||||||
for subelem_tag in ('instance_id', 'name', 'hours', 'memory_mb',
|
|
||||||
'local_gb', 'vcpus', 'tenant_id', 'flavor',
|
|
||||||
'started_at', 'ended_at', 'state', 'uptime'):
|
|
||||||
subelem = xmlutil.SubTemplateElement(server_usage, subelem_tag)
|
|
||||||
subelem.text = subelem_tag
|
|
||||||
|
|
||||||
|
|
||||||
def parse_strtime(dstr, fmt):
|
def parse_strtime(dstr, fmt):
|
||||||
try:
|
try:
|
||||||
return timeutils.parse_strtime(dstr, fmt)
|
return timeutils.parse_strtime(dstr, fmt)
|
||||||
|
@ -58,22 +39,6 @@ def parse_strtime(dstr, fmt):
|
||||||
raise exception.InvalidStrTime(reason=six.text_type(e))
|
raise exception.InvalidStrTime(reason=six.text_type(e))
|
||||||
|
|
||||||
|
|
||||||
class SimpleTenantUsageTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('tenant_usage', selector='tenant_usage')
|
|
||||||
make_usage(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleTenantUsagesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('tenant_usages')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'tenant_usage',
|
|
||||||
selector='tenant_usages')
|
|
||||||
make_usage(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleTenantUsageController(object):
|
class SimpleTenantUsageController(object):
|
||||||
def _hours_for(self, instance, period_start, period_stop):
|
def _hours_for(self, instance, period_start, period_stop):
|
||||||
launched_at = instance.launched_at
|
launched_at = instance.launched_at
|
||||||
|
@ -254,7 +219,6 @@ class SimpleTenantUsageController(object):
|
||||||
detailed = env.get('detailed', ['0'])[0] == '1'
|
detailed = env.get('detailed', ['0'])[0] == '1'
|
||||||
return (period_start, period_stop, detailed)
|
return (period_start, period_stop, detailed)
|
||||||
|
|
||||||
@wsgi.serializers(xml=SimpleTenantUsagesTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Retrieve tenant_usage for all tenants."""
|
"""Retrieve tenant_usage for all tenants."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -276,7 +240,6 @@ class SimpleTenantUsageController(object):
|
||||||
detailed=detailed)
|
detailed=detailed)
|
||||||
return {'tenant_usages': usages}
|
return {'tenant_usages': usages}
|
||||||
|
|
||||||
@wsgi.serializers(xml=SimpleTenantUsageTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Retrieve tenant_usage for a specified tenant."""
|
"""Retrieve tenant_usage for a specified tenant."""
|
||||||
tenant_id = id
|
tenant_id = id
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import quota
|
from nova import quota
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,13 +27,6 @@ authorize_for_admin = extensions.extension_authorizer('compute',
|
||||||
'used_limits_for_admin')
|
'used_limits_for_admin')
|
||||||
|
|
||||||
|
|
||||||
class UsedLimitsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('limits', selector='limits')
|
|
||||||
root.set('{%s}usedLimits' % XMLNS, '%s:usedLimits' % ALIAS)
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS})
|
|
||||||
|
|
||||||
|
|
||||||
class UsedLimitsController(wsgi.Controller):
|
class UsedLimitsController(wsgi.Controller):
|
||||||
|
|
||||||
def __init__(self, ext_mgr):
|
def __init__(self, ext_mgr):
|
||||||
|
@ -49,7 +41,7 @@ class UsedLimitsController(wsgi.Controller):
|
||||||
|
|
||||||
@wsgi.extends
|
@wsgi.extends
|
||||||
def index(self, req, resp_obj):
|
def index(self, req, resp_obj):
|
||||||
resp_obj.attach(xml=UsedLimitsTemplate())
|
resp_obj.attach()
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
project_id = self._project_id(context, req)
|
project_id = self._project_id(context, req)
|
||||||
quotas = QUOTAS.get_project_quotas(context, project_id, usages=True)
|
quotas = QUOTAS.get_project_quotas(context, project_id, usages=True)
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import network
|
from nova import network
|
||||||
|
|
||||||
|
@ -29,16 +28,6 @@ authorize = extensions.extension_authorizer('compute', 'virtual_interfaces')
|
||||||
vif_nsmap = {None: wsgi.XMLNS_V11}
|
vif_nsmap = {None: wsgi.XMLNS_V11}
|
||||||
|
|
||||||
|
|
||||||
class VirtualInterfaceTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('virtual_interfaces')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'virtual_interface',
|
|
||||||
selector='virtual_interfaces')
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('mac_address')
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=vif_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
def _translate_vif_summary_view(_context, vif):
|
def _translate_vif_summary_view(_context, vif):
|
||||||
"""Maps keys for VIF summary view."""
|
"""Maps keys for VIF summary view."""
|
||||||
d = {}
|
d = {}
|
||||||
|
@ -67,7 +56,6 @@ class ServerVirtualInterfaceController(object):
|
||||||
res = [entity_maker(context, vif) for vif in limited_list]
|
res = [entity_maker(context, vif) for vif in limited_list]
|
||||||
return {'virtual_interfaces': res}
|
return {'virtual_interfaces': res}
|
||||||
|
|
||||||
@wsgi.serializers(xml=VirtualInterfaceTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
"""Returns the list of VIFs for a given instance."""
|
"""Returns the list of VIFs for a given instance."""
|
||||||
authorize(req.environ['nova.context'])
|
authorize(req.environ['nova.context'])
|
||||||
|
|
|
@ -22,7 +22,6 @@ from webob import exc
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -84,82 +83,6 @@ def _translate_volume_summary_view(context, vol):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def make_volume(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('status')
|
|
||||||
elem.set('size')
|
|
||||||
elem.set('availabilityZone')
|
|
||||||
elem.set('createdAt')
|
|
||||||
elem.set('displayName')
|
|
||||||
elem.set('displayDescription')
|
|
||||||
elem.set('volumeType')
|
|
||||||
elem.set('snapshotId')
|
|
||||||
|
|
||||||
attachments = xmlutil.SubTemplateElement(elem, 'attachments')
|
|
||||||
attachment = xmlutil.SubTemplateElement(attachments, 'attachment',
|
|
||||||
selector='attachments')
|
|
||||||
make_attachment(attachment)
|
|
||||||
|
|
||||||
# Attach metadata node
|
|
||||||
elem.append(common.MetadataTemplate())
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('volume', selector='volume')
|
|
||||||
make_volume(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class VolumesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('volumes')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'volume', selector='volumes')
|
|
||||||
make_volume(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class CommonDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
"""Common deserializer to handle xml-formatted volume requests.
|
|
||||||
|
|
||||||
Handles standard volume attributes as well as the optional metadata
|
|
||||||
attribute
|
|
||||||
"""
|
|
||||||
|
|
||||||
metadata_deserializer = common.MetadataXMLDeserializer()
|
|
||||||
|
|
||||||
def _extract_volume(self, node):
|
|
||||||
"""Marshal the volume attribute of a parsed request."""
|
|
||||||
vol = {}
|
|
||||||
volume_node = self.find_first_child_named(node, 'volume')
|
|
||||||
|
|
||||||
attributes = ['display_name', 'display_description', 'size',
|
|
||||||
'volume_type', 'availability_zone']
|
|
||||||
for attr in attributes:
|
|
||||||
if volume_node.getAttribute(attr):
|
|
||||||
vol[attr] = volume_node.getAttribute(attr)
|
|
||||||
|
|
||||||
metadata_node = self.find_first_child_named(volume_node, 'metadata')
|
|
||||||
if metadata_node is not None:
|
|
||||||
vol['metadata'] = self.extract_metadata(metadata_node)
|
|
||||||
|
|
||||||
return vol
|
|
||||||
|
|
||||||
|
|
||||||
class CreateDeserializer(CommonDeserializer):
|
|
||||||
"""Deserializer to handle xml-formatted create volume requests.
|
|
||||||
|
|
||||||
Handles standard volume attributes as well as the optional metadata
|
|
||||||
attribute
|
|
||||||
"""
|
|
||||||
|
|
||||||
def default(self, string):
|
|
||||||
"""Deserialize an xml-formatted volume create request."""
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
vol = self._extract_volume(dom)
|
|
||||||
return {'body': {'volume': vol}}
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeController(wsgi.Controller):
|
class VolumeController(wsgi.Controller):
|
||||||
"""The Volumes API controller for the OpenStack API."""
|
"""The Volumes API controller for the OpenStack API."""
|
||||||
|
|
||||||
|
@ -167,7 +90,6 @@ class VolumeController(wsgi.Controller):
|
||||||
self.volume_api = volume.API()
|
self.volume_api = volume.API()
|
||||||
super(VolumeController, self).__init__()
|
super(VolumeController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=VolumeTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return data about the given volume."""
|
"""Return data about the given volume."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -193,12 +115,10 @@ class VolumeController(wsgi.Controller):
|
||||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||||
return webob.Response(status_int=202)
|
return webob.Response(status_int=202)
|
||||||
|
|
||||||
@wsgi.serializers(xml=VolumesTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Returns a summary list of volumes."""
|
"""Returns a summary list of volumes."""
|
||||||
return self._items(req, entity_maker=_translate_volume_summary_view)
|
return self._items(req, entity_maker=_translate_volume_summary_view)
|
||||||
|
|
||||||
@wsgi.serializers(xml=VolumesTemplate)
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Returns a detailed list of volumes."""
|
"""Returns a detailed list of volumes."""
|
||||||
return self._items(req, entity_maker=_translate_volume_detail_view)
|
return self._items(req, entity_maker=_translate_volume_detail_view)
|
||||||
|
@ -213,8 +133,6 @@ class VolumeController(wsgi.Controller):
|
||||||
res = [entity_maker(context, vol) for vol in limited_list]
|
res = [entity_maker(context, vol) for vol in limited_list]
|
||||||
return {'volumes': res}
|
return {'volumes': res}
|
||||||
|
|
||||||
@wsgi.serializers(xml=VolumeTemplate)
|
|
||||||
@wsgi.deserializers(xml=CreateDeserializer)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Creates a new volume."""
|
"""Creates a new volume."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -297,30 +215,6 @@ def _translate_attachment_summary_view(volume_id, instance_uuid, mountpoint):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def make_attachment(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('serverId')
|
|
||||||
elem.set('volumeId')
|
|
||||||
elem.set('device')
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeAttachmentTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('volumeAttachment',
|
|
||||||
selector='volumeAttachment')
|
|
||||||
make_attachment(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeAttachmentsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('volumeAttachments')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'volumeAttachment',
|
|
||||||
selector='volumeAttachments')
|
|
||||||
make_attachment(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeAttachmentController(wsgi.Controller):
|
class VolumeAttachmentController(wsgi.Controller):
|
||||||
"""The volume attachment API controller for the OpenStack API.
|
"""The volume attachment API controller for the OpenStack API.
|
||||||
|
|
||||||
|
@ -335,7 +229,6 @@ class VolumeAttachmentController(wsgi.Controller):
|
||||||
self.ext_mgr = ext_mgr
|
self.ext_mgr = ext_mgr
|
||||||
super(VolumeAttachmentController, self).__init__()
|
super(VolumeAttachmentController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=VolumeAttachmentsTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
"""Returns the list of volume attachments for a given instance."""
|
"""Returns the list of volume attachments for a given instance."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -343,7 +236,6 @@ class VolumeAttachmentController(wsgi.Controller):
|
||||||
return self._items(req, server_id,
|
return self._items(req, server_id,
|
||||||
entity_maker=_translate_attachment_summary_view)
|
entity_maker=_translate_attachment_summary_view)
|
||||||
|
|
||||||
@wsgi.serializers(xml=VolumeAttachmentTemplate)
|
|
||||||
def show(self, req, server_id, id):
|
def show(self, req, server_id, id):
|
||||||
"""Return data about the given volume attachment."""
|
"""Return data about the given volume attachment."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -381,7 +273,6 @@ class VolumeAttachmentController(wsgi.Controller):
|
||||||
"not in proper format (%s)") % volume_id
|
"not in proper format (%s)") % volume_id
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
@wsgi.serializers(xml=VolumeAttachmentTemplate)
|
|
||||||
def create(self, req, server_id, body):
|
def create(self, req, server_id, body):
|
||||||
"""Attach a volume to an instance."""
|
"""Attach a volume to an instance."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -585,32 +476,6 @@ def _translate_snapshot_summary_view(context, vol):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def make_snapshot(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('status')
|
|
||||||
elem.set('size')
|
|
||||||
elem.set('createdAt')
|
|
||||||
elem.set('displayName')
|
|
||||||
elem.set('displayDescription')
|
|
||||||
elem.set('volumeId')
|
|
||||||
|
|
||||||
|
|
||||||
class SnapshotTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('snapshot', selector='snapshot')
|
|
||||||
make_snapshot(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class SnapshotsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('snapshots')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'snapshot',
|
|
||||||
selector='snapshots')
|
|
||||||
make_snapshot(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class SnapshotController(wsgi.Controller):
|
class SnapshotController(wsgi.Controller):
|
||||||
"""The Snapshots API controller for the OpenStack API."""
|
"""The Snapshots API controller for the OpenStack API."""
|
||||||
|
|
||||||
|
@ -618,7 +483,6 @@ class SnapshotController(wsgi.Controller):
|
||||||
self.volume_api = volume.API()
|
self.volume_api = volume.API()
|
||||||
super(SnapshotController, self).__init__()
|
super(SnapshotController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=SnapshotTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return data about the given snapshot."""
|
"""Return data about the given snapshot."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -644,12 +508,10 @@ class SnapshotController(wsgi.Controller):
|
||||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||||
return webob.Response(status_int=202)
|
return webob.Response(status_int=202)
|
||||||
|
|
||||||
@wsgi.serializers(xml=SnapshotsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Returns a summary list of snapshots."""
|
"""Returns a summary list of snapshots."""
|
||||||
return self._items(req, entity_maker=_translate_snapshot_summary_view)
|
return self._items(req, entity_maker=_translate_snapshot_summary_view)
|
||||||
|
|
||||||
@wsgi.serializers(xml=SnapshotsTemplate)
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Returns a detailed list of snapshots."""
|
"""Returns a detailed list of snapshots."""
|
||||||
return self._items(req, entity_maker=_translate_snapshot_detail_view)
|
return self._items(req, entity_maker=_translate_snapshot_detail_view)
|
||||||
|
@ -664,7 +526,6 @@ class SnapshotController(wsgi.Controller):
|
||||||
res = [entity_maker(context, snapshot) for snapshot in limited_list]
|
res = [entity_maker(context, snapshot) for snapshot in limited_list]
|
||||||
return {'snapshots': res}
|
return {'snapshots': res}
|
||||||
|
|
||||||
@wsgi.serializers(xml=SnapshotTemplate)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Creates a new snapshot."""
|
"""Creates a new snapshot."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -19,48 +19,13 @@ import webob
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack.compute.views import flavors as flavors_view
|
from nova.api.openstack.compute.views import flavors as flavors_view
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.compute import flavors
|
from nova.compute import flavors
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
|
||||||
|
|
||||||
def make_flavor(elem, detailed=False):
|
flavor_nsmap = {}
|
||||||
elem.set('name')
|
|
||||||
elem.set('id')
|
|
||||||
if detailed:
|
|
||||||
elem.set('ram')
|
|
||||||
elem.set('disk')
|
|
||||||
elem.set('vcpus', xmlutil.EmptyStringSelector('vcpus'))
|
|
||||||
|
|
||||||
xmlutil.make_links(elem, 'links')
|
|
||||||
|
|
||||||
|
|
||||||
flavor_nsmap = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavor', selector='flavor')
|
|
||||||
make_flavor(root, detailed=True)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=flavor_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class MinimalFlavorsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'flavor', selector='flavors')
|
|
||||||
make_flavor(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=flavor_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('flavors')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'flavor', selector='flavors')
|
|
||||||
make_flavor(elem, detailed=True)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=flavor_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
|
@ -68,20 +33,17 @@ class Controller(wsgi.Controller):
|
||||||
|
|
||||||
_view_builder_class = flavors_view.ViewBuilder
|
_view_builder_class = flavors_view.ViewBuilder
|
||||||
|
|
||||||
@wsgi.serializers(xml=MinimalFlavorsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return all flavors in brief."""
|
"""Return all flavors in brief."""
|
||||||
limited_flavors = self._get_flavors(req)
|
limited_flavors = self._get_flavors(req)
|
||||||
return self._view_builder.index(req, limited_flavors)
|
return self._view_builder.index(req, limited_flavors)
|
||||||
|
|
||||||
@wsgi.serializers(xml=FlavorsTemplate)
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Return all flavors in detail."""
|
"""Return all flavors in detail."""
|
||||||
limited_flavors = self._get_flavors(req)
|
limited_flavors = self._get_flavors(req)
|
||||||
req.cache_db_flavors(limited_flavors)
|
req.cache_db_flavors(limited_flavors)
|
||||||
return self._view_builder.detail(req, limited_flavors)
|
return self._view_builder.detail(req, limited_flavors)
|
||||||
|
|
||||||
@wsgi.serializers(xml=FlavorTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return data about the given flavor id."""
|
"""Return data about the given flavor id."""
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -37,14 +37,12 @@ class Controller(object):
|
||||||
msg = _("Image not found.")
|
msg = _("Image not found.")
|
||||||
raise exc.HTTPNotFound(explanation=msg)
|
raise exc.HTTPNotFound(explanation=msg)
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
|
||||||
def index(self, req, image_id):
|
def index(self, req, image_id):
|
||||||
"""Returns the list of metadata for a given instance."""
|
"""Returns the list of metadata for a given instance."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
metadata = self._get_image(context, image_id)['properties']
|
metadata = self._get_image(context, image_id)['properties']
|
||||||
return dict(metadata=metadata)
|
return dict(metadata=metadata)
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetaItemTemplate)
|
|
||||||
def show(self, req, image_id, id):
|
def show(self, req, image_id, id):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
metadata = self._get_image(context, image_id)['properties']
|
metadata = self._get_image(context, image_id)['properties']
|
||||||
|
@ -53,8 +51,6 @@ class Controller(object):
|
||||||
else:
|
else:
|
||||||
raise exc.HTTPNotFound()
|
raise exc.HTTPNotFound()
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
|
||||||
@wsgi.deserializers(xml=common.MetadataDeserializer)
|
|
||||||
def create(self, req, image_id, body):
|
def create(self, req, image_id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
image = self._get_image(context, image_id)
|
image = self._get_image(context, image_id)
|
||||||
|
@ -70,8 +66,6 @@ class Controller(object):
|
||||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||||
return dict(metadata=image['properties'])
|
return dict(metadata=image['properties'])
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetaItemTemplate)
|
|
||||||
@wsgi.deserializers(xml=common.MetaItemDeserializer)
|
|
||||||
def update(self, req, image_id, id, body):
|
def update(self, req, image_id, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
||||||
|
@ -99,8 +93,6 @@ class Controller(object):
|
||||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||||
return dict(meta=meta)
|
return dict(meta=meta)
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
|
||||||
@wsgi.deserializers(xml=common.MetadataDeserializer)
|
|
||||||
def update_all(self, req, image_id, body):
|
def update_all(self, req, image_id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
image = self._get_image(context, image_id)
|
image = self._get_image(context, image_id)
|
||||||
|
|
|
@ -18,7 +18,6 @@ import webob.exc
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack.compute.views import images as views_images
|
from nova.api.openstack.compute.views import images as views_images
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
import nova.image
|
import nova.image
|
||||||
|
@ -36,52 +35,7 @@ SUPPORTED_FILTERS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_image(elem, detailed=False):
|
image_nsmap = {}
|
||||||
elem.set('name')
|
|
||||||
elem.set('id')
|
|
||||||
|
|
||||||
if detailed:
|
|
||||||
elem.set('updated')
|
|
||||||
elem.set('created')
|
|
||||||
elem.set('status')
|
|
||||||
elem.set('progress')
|
|
||||||
elem.set('minRam')
|
|
||||||
elem.set('minDisk')
|
|
||||||
|
|
||||||
server = xmlutil.SubTemplateElement(elem, 'server', selector='server')
|
|
||||||
server.set('id')
|
|
||||||
xmlutil.make_links(server, 'links')
|
|
||||||
|
|
||||||
elem.append(common.MetadataTemplate())
|
|
||||||
|
|
||||||
xmlutil.make_links(elem, 'links')
|
|
||||||
|
|
||||||
|
|
||||||
image_nsmap = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
|
|
||||||
|
|
||||||
|
|
||||||
class ImageTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('image', selector='image')
|
|
||||||
make_image(root, detailed=True)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=image_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class MinimalImagesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('images')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'image', selector='images')
|
|
||||||
make_image(elem)
|
|
||||||
xmlutil.make_links(root, 'images_links')
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=image_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ImagesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('images')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'image', selector='images')
|
|
||||||
make_image(elem, detailed=True)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=image_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
|
@ -120,7 +74,6 @@ class Controller(wsgi.Controller):
|
||||||
|
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
@wsgi.serializers(xml=ImageTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return detailed information about a specific image.
|
"""Return detailed information about a specific image.
|
||||||
|
|
||||||
|
@ -157,7 +110,6 @@ class Controller(wsgi.Controller):
|
||||||
raise webob.exc.HTTPForbidden(explanation=explanation)
|
raise webob.exc.HTTPForbidden(explanation=explanation)
|
||||||
return webob.exc.HTTPNoContent()
|
return webob.exc.HTTPNoContent()
|
||||||
|
|
||||||
@wsgi.serializers(xml=MinimalImagesTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return an index listing of images available to the request.
|
"""Return an index listing of images available to the request.
|
||||||
|
|
||||||
|
@ -178,7 +130,6 @@ class Controller(wsgi.Controller):
|
||||||
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
|
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
|
||||||
return self._view_builder.index(req, images)
|
return self._view_builder.index(req, images)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ImagesTemplate)
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Return a detailed index listing of images available to the request.
|
"""Return a detailed index listing of images available to the request.
|
||||||
|
|
||||||
|
|
|
@ -19,36 +19,10 @@ import nova
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack.compute.views import addresses as view_addresses
|
from nova.api.openstack.compute.views import addresses as view_addresses
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
|
||||||
|
|
||||||
def make_network(elem):
|
network_nsmap = {}
|
||||||
elem.set('id', 0)
|
|
||||||
|
|
||||||
ip = xmlutil.SubTemplateElement(elem, 'ip', selector=1)
|
|
||||||
ip.set('version')
|
|
||||||
ip.set('addr')
|
|
||||||
|
|
||||||
|
|
||||||
network_nsmap = {None: xmlutil.XMLNS_V11}
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
sel = xmlutil.Selector(xmlutil.get_items, 0)
|
|
||||||
root = xmlutil.TemplateElement('network', selector=sel)
|
|
||||||
make_network(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=network_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class AddressesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('addresses', selector='addresses')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'network',
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
make_network(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=network_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
|
@ -60,14 +34,12 @@ class Controller(wsgi.Controller):
|
||||||
super(Controller, self).__init__(**kwargs)
|
super(Controller, self).__init__(**kwargs)
|
||||||
self._compute_api = nova.compute.API()
|
self._compute_api = nova.compute.API()
|
||||||
|
|
||||||
@wsgi.serializers(xml=AddressesTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
context = req.environ["nova.context"]
|
context = req.environ["nova.context"]
|
||||||
instance = common.get_instance(self._compute_api, context, server_id)
|
instance = common.get_instance(self._compute_api, context, server_id)
|
||||||
networks = common.get_networks_for_instance(context, instance)
|
networks = common.get_networks_for_instance(context, instance)
|
||||||
return self._view_builder.index(networks)
|
return self._view_builder.index(networks)
|
||||||
|
|
||||||
@wsgi.serializers(xml=NetworkTemplate)
|
|
||||||
def show(self, req, server_id, id):
|
def show(self, req, server_id, id):
|
||||||
context = req.environ["nova.context"]
|
context = req.environ["nova.context"]
|
||||||
instance = common.get_instance(self._compute_api, context, server_id)
|
instance = common.get_instance(self._compute_api, context, server_id)
|
||||||
|
|
|
@ -45,7 +45,6 @@ import webob.exc
|
||||||
|
|
||||||
from nova.api.openstack.compute.views import limits as limits_views
|
from nova.api.openstack.compute.views import limits as limits_views
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import quota
|
from nova import quota
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
@ -56,38 +55,12 @@ QUOTAS = quota.QUOTAS
|
||||||
LIMITS_PREFIX = "limits."
|
LIMITS_PREFIX = "limits."
|
||||||
|
|
||||||
|
|
||||||
limits_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
|
limits_nsmap = {}
|
||||||
|
|
||||||
|
|
||||||
class LimitsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('limits', selector='limits')
|
|
||||||
|
|
||||||
rates = xmlutil.SubTemplateElement(root, 'rates')
|
|
||||||
rate = xmlutil.SubTemplateElement(rates, 'rate', selector='rate')
|
|
||||||
rate.set('uri', 'uri')
|
|
||||||
rate.set('regex', 'regex')
|
|
||||||
limit = xmlutil.SubTemplateElement(rate, 'limit', selector='limit')
|
|
||||||
limit.set('value', 'value')
|
|
||||||
limit.set('verb', 'verb')
|
|
||||||
limit.set('remaining', 'remaining')
|
|
||||||
limit.set('unit', 'unit')
|
|
||||||
limit.set('next-available', 'next-available')
|
|
||||||
|
|
||||||
absolute = xmlutil.SubTemplateElement(root, 'absolute',
|
|
||||||
selector='absolute')
|
|
||||||
limit = xmlutil.SubTemplateElement(absolute, 'limit',
|
|
||||||
selector=xmlutil.get_items)
|
|
||||||
limit.set('name', 0)
|
|
||||||
limit.set('value', 1)
|
|
||||||
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=limits_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class LimitsController(object):
|
class LimitsController(object):
|
||||||
"""Controller for accessing limits in the OpenStack API."""
|
"""Controller for accessing limits in the OpenStack API."""
|
||||||
|
|
||||||
@wsgi.serializers(xml=LimitsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return all global and rate limit information."""
|
"""Return all global and rate limit information."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -55,13 +55,6 @@ CONF.import_opt('admin_tenant_name',
|
||||||
CONF.import_opt('compute_driver', 'nova.virt.driver')
|
CONF.import_opt('compute_driver', 'nova.virt.driver')
|
||||||
|
|
||||||
|
|
||||||
def _interface_dict(interface_ref):
|
|
||||||
d = {}
|
|
||||||
for f in interface_fields:
|
|
||||||
d[f] = interface_ref.get(f)
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def _get_ironic_client():
|
def _get_ironic_client():
|
||||||
"""return an Ironic client."""
|
"""return an Ironic client."""
|
||||||
# TODO(NobodyCam): Fix insecure setting
|
# TODO(NobodyCam): Fix insecure setting
|
||||||
|
|
|
@ -1,141 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
-*- rnc -*-
|
|
||||||
RELAX NG Compact Syntax Grammar for the
|
|
||||||
Atom Format Specification Version 11
|
|
||||||
-->
|
|
||||||
<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">
|
|
||||||
<start>
|
|
||||||
<choice>
|
|
||||||
<ref name="atomLink"/>
|
|
||||||
</choice>
|
|
||||||
</start>
|
|
||||||
<!-- Common attributes -->
|
|
||||||
<define name="atomCommonAttributes">
|
|
||||||
<optional>
|
|
||||||
<attribute name="xml:base">
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="xml:lang">
|
|
||||||
<ref name="atomLanguageTag"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="undefinedAttribute"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</define>
|
|
||||||
<!-- atom:link -->
|
|
||||||
<define name="atomLink">
|
|
||||||
<element name="atom:link">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<attribute name="href">
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</attribute>
|
|
||||||
<optional>
|
|
||||||
<attribute name="rel">
|
|
||||||
<choice>
|
|
||||||
<ref name="atomNCName"/>
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</choice>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="type">
|
|
||||||
<ref name="atomMediaType"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="hreflang">
|
|
||||||
<ref name="atomLanguageTag"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="title"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="length"/>
|
|
||||||
</optional>
|
|
||||||
<ref name="undefinedContent"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- Low-level simple types -->
|
|
||||||
<define name="atomNCName">
|
|
||||||
<data type="string">
|
|
||||||
<param name="minLength">1</param>
|
|
||||||
<param name="pattern">[^:]*</param>
|
|
||||||
</data>
|
|
||||||
</define>
|
|
||||||
<!-- Whatever a media type is, it contains at least one slash -->
|
|
||||||
<define name="atomMediaType">
|
|
||||||
<data type="string">
|
|
||||||
<param name="pattern">.+/.+</param>
|
|
||||||
</data>
|
|
||||||
</define>
|
|
||||||
<!-- As defined in RFC 3066 -->
|
|
||||||
<define name="atomLanguageTag">
|
|
||||||
<data type="string">
|
|
||||||
<param name="pattern">[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*</param>
|
|
||||||
</data>
|
|
||||||
</define>
|
|
||||||
<!--
|
|
||||||
Unconstrained; it's not entirely clear how IRI fit into
|
|
||||||
xsd:anyURI so let's not try to constrain it here
|
|
||||||
-->
|
|
||||||
<define name="atomUri">
|
|
||||||
<text/>
|
|
||||||
</define>
|
|
||||||
<!-- Other Extensibility -->
|
|
||||||
<define name="undefinedAttribute">
|
|
||||||
<attribute>
|
|
||||||
<anyName>
|
|
||||||
<except>
|
|
||||||
<name>xml:base</name>
|
|
||||||
<name>xml:lang</name>
|
|
||||||
<nsName ns=""/>
|
|
||||||
</except>
|
|
||||||
</anyName>
|
|
||||||
</attribute>
|
|
||||||
</define>
|
|
||||||
<define name="undefinedContent">
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyForeignElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</define>
|
|
||||||
<define name="anyElement">
|
|
||||||
<element>
|
|
||||||
<anyName/>
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<attribute>
|
|
||||||
<anyName/>
|
|
||||||
</attribute>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<define name="anyForeignElement">
|
|
||||||
<element>
|
|
||||||
<anyName>
|
|
||||||
<except>
|
|
||||||
<nsName ns="http://www.w3.org/2005/Atom"/>
|
|
||||||
</except>
|
|
||||||
</anyName>
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<attribute>
|
|
||||||
<anyName/>
|
|
||||||
</attribute>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
</grammar>
|
|
|
@ -1,597 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
-*- rnc -*-
|
|
||||||
RELAX NG Compact Syntax Grammar for the
|
|
||||||
Atom Format Specification Version 11
|
|
||||||
-->
|
|
||||||
<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">
|
|
||||||
<start>
|
|
||||||
<choice>
|
|
||||||
<ref name="atomFeed"/>
|
|
||||||
<ref name="atomEntry"/>
|
|
||||||
</choice>
|
|
||||||
</start>
|
|
||||||
<!-- Common attributes -->
|
|
||||||
<define name="atomCommonAttributes">
|
|
||||||
<optional>
|
|
||||||
<attribute name="xml:base">
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="xml:lang">
|
|
||||||
<ref name="atomLanguageTag"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="undefinedAttribute"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</define>
|
|
||||||
<!-- Text Constructs -->
|
|
||||||
<define name="atomPlainTextConstruct">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<optional>
|
|
||||||
<attribute name="type">
|
|
||||||
<choice>
|
|
||||||
<value>text</value>
|
|
||||||
<value>html</value>
|
|
||||||
</choice>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<text/>
|
|
||||||
</define>
|
|
||||||
<define name="atomXHTMLTextConstruct">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<attribute name="type">
|
|
||||||
<value>xhtml</value>
|
|
||||||
</attribute>
|
|
||||||
<ref name="xhtmlDiv"/>
|
|
||||||
</define>
|
|
||||||
<define name="atomTextConstruct">
|
|
||||||
<choice>
|
|
||||||
<ref name="atomPlainTextConstruct"/>
|
|
||||||
<ref name="atomXHTMLTextConstruct"/>
|
|
||||||
</choice>
|
|
||||||
</define>
|
|
||||||
<!-- Person Construct -->
|
|
||||||
<define name="atomPersonConstruct">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<interleave>
|
|
||||||
<element name="atom:name">
|
|
||||||
<text/>
|
|
||||||
</element>
|
|
||||||
<optional>
|
|
||||||
<element name="atom:uri">
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</element>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<element name="atom:email">
|
|
||||||
<ref name="atomEmailAddress"/>
|
|
||||||
</element>
|
|
||||||
</optional>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="extensionElement"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</interleave>
|
|
||||||
</define>
|
|
||||||
<!-- Date Construct -->
|
|
||||||
<define name="atomDateConstruct">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<data type="dateTime"/>
|
|
||||||
</define>
|
|
||||||
<!-- atom:feed -->
|
|
||||||
<define name="atomFeed">
|
|
||||||
<element name="atom:feed">
|
|
||||||
<s:rule context="atom:feed">
|
|
||||||
<s:assert test="atom:author or not(atom:entry[not(atom:author)])">An atom:feed must have an atom:author unless all of its atom:entry children have an atom:author.</s:assert>
|
|
||||||
</s:rule>
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<interleave>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomAuthor"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomCategory"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomContributor"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomGenerator"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomIcon"/>
|
|
||||||
</optional>
|
|
||||||
<ref name="atomId"/>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomLink"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomLogo"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomRights"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomSubtitle"/>
|
|
||||||
</optional>
|
|
||||||
<ref name="atomTitle"/>
|
|
||||||
<ref name="atomUpdated"/>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="extensionElement"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</interleave>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomEntry"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:entry -->
|
|
||||||
<define name="atomEntry">
|
|
||||||
<element name="atom:entry">
|
|
||||||
<s:rule context="atom:entry">
|
|
||||||
<s:assert test="atom:link[@rel='alternate'] or atom:link[not(@rel)] or atom:content">An atom:entry must have at least one atom:link element with a rel attribute of 'alternate' or an atom:content.</s:assert>
|
|
||||||
</s:rule>
|
|
||||||
<s:rule context="atom:entry">
|
|
||||||
<s:assert test="atom:author or ../atom:author or atom:source/atom:author">An atom:entry must have an atom:author if its feed does not.</s:assert>
|
|
||||||
</s:rule>
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<interleave>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomAuthor"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomCategory"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomContent"/>
|
|
||||||
</optional>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomContributor"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<ref name="atomId"/>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomLink"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomPublished"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomRights"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomSource"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomSummary"/>
|
|
||||||
</optional>
|
|
||||||
<ref name="atomTitle"/>
|
|
||||||
<ref name="atomUpdated"/>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="extensionElement"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</interleave>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:content -->
|
|
||||||
<define name="atomInlineTextContent">
|
|
||||||
<element name="atom:content">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<optional>
|
|
||||||
<attribute name="type">
|
|
||||||
<choice>
|
|
||||||
<value>text</value>
|
|
||||||
<value>html</value>
|
|
||||||
</choice>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<zeroOrMore>
|
|
||||||
<text/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<define name="atomInlineXHTMLContent">
|
|
||||||
<element name="atom:content">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<attribute name="type">
|
|
||||||
<value>xhtml</value>
|
|
||||||
</attribute>
|
|
||||||
<ref name="xhtmlDiv"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<define name="atomInlineOtherContent">
|
|
||||||
<element name="atom:content">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<optional>
|
|
||||||
<attribute name="type">
|
|
||||||
<ref name="atomMediaType"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<define name="atomOutOfLineContent">
|
|
||||||
<element name="atom:content">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<optional>
|
|
||||||
<attribute name="type">
|
|
||||||
<ref name="atomMediaType"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<attribute name="src">
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</attribute>
|
|
||||||
<empty/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<define name="atomContent">
|
|
||||||
<choice>
|
|
||||||
<ref name="atomInlineTextContent"/>
|
|
||||||
<ref name="atomInlineXHTMLContent"/>
|
|
||||||
<ref name="atomInlineOtherContent"/>
|
|
||||||
<ref name="atomOutOfLineContent"/>
|
|
||||||
</choice>
|
|
||||||
</define>
|
|
||||||
<!-- atom:author -->
|
|
||||||
<define name="atomAuthor">
|
|
||||||
<element name="atom:author">
|
|
||||||
<ref name="atomPersonConstruct"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:category -->
|
|
||||||
<define name="atomCategory">
|
|
||||||
<element name="atom:category">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<attribute name="term"/>
|
|
||||||
<optional>
|
|
||||||
<attribute name="scheme">
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="label"/>
|
|
||||||
</optional>
|
|
||||||
<ref name="undefinedContent"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:contributor -->
|
|
||||||
<define name="atomContributor">
|
|
||||||
<element name="atom:contributor">
|
|
||||||
<ref name="atomPersonConstruct"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:generator -->
|
|
||||||
<define name="atomGenerator">
|
|
||||||
<element name="atom:generator">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<optional>
|
|
||||||
<attribute name="uri">
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="version"/>
|
|
||||||
</optional>
|
|
||||||
<text/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:icon -->
|
|
||||||
<define name="atomIcon">
|
|
||||||
<element name="atom:icon">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:id -->
|
|
||||||
<define name="atomId">
|
|
||||||
<element name="atom:id">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:logo -->
|
|
||||||
<define name="atomLogo">
|
|
||||||
<element name="atom:logo">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:link -->
|
|
||||||
<define name="atomLink">
|
|
||||||
<element name="atom:link">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<attribute name="href">
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</attribute>
|
|
||||||
<optional>
|
|
||||||
<attribute name="rel">
|
|
||||||
<choice>
|
|
||||||
<ref name="atomNCName"/>
|
|
||||||
<ref name="atomUri"/>
|
|
||||||
</choice>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="type">
|
|
||||||
<ref name="atomMediaType"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="hreflang">
|
|
||||||
<ref name="atomLanguageTag"/>
|
|
||||||
</attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="title"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="length"/>
|
|
||||||
</optional>
|
|
||||||
<ref name="undefinedContent"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:published -->
|
|
||||||
<define name="atomPublished">
|
|
||||||
<element name="atom:published">
|
|
||||||
<ref name="atomDateConstruct"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:rights -->
|
|
||||||
<define name="atomRights">
|
|
||||||
<element name="atom:rights">
|
|
||||||
<ref name="atomTextConstruct"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:source -->
|
|
||||||
<define name="atomSource">
|
|
||||||
<element name="atom:source">
|
|
||||||
<ref name="atomCommonAttributes"/>
|
|
||||||
<interleave>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomAuthor"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomCategory"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomContributor"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomGenerator"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomIcon"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomId"/>
|
|
||||||
</optional>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="atomLink"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomLogo"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomRights"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomSubtitle"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomTitle"/>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<ref name="atomUpdated"/>
|
|
||||||
</optional>
|
|
||||||
<zeroOrMore>
|
|
||||||
<ref name="extensionElement"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</interleave>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:subtitle -->
|
|
||||||
<define name="atomSubtitle">
|
|
||||||
<element name="atom:subtitle">
|
|
||||||
<ref name="atomTextConstruct"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:summary -->
|
|
||||||
<define name="atomSummary">
|
|
||||||
<element name="atom:summary">
|
|
||||||
<ref name="atomTextConstruct"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:title -->
|
|
||||||
<define name="atomTitle">
|
|
||||||
<element name="atom:title">
|
|
||||||
<ref name="atomTextConstruct"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- atom:updated -->
|
|
||||||
<define name="atomUpdated">
|
|
||||||
<element name="atom:updated">
|
|
||||||
<ref name="atomDateConstruct"/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- Low-level simple types -->
|
|
||||||
<define name="atomNCName">
|
|
||||||
<data type="string">
|
|
||||||
<param name="minLength">1</param>
|
|
||||||
<param name="pattern">[^:]*</param>
|
|
||||||
</data>
|
|
||||||
</define>
|
|
||||||
<!-- Whatever a media type is, it contains at least one slash -->
|
|
||||||
<define name="atomMediaType">
|
|
||||||
<data type="string">
|
|
||||||
<param name="pattern">.+/.+</param>
|
|
||||||
</data>
|
|
||||||
</define>
|
|
||||||
<!-- As defined in RFC 3066 -->
|
|
||||||
<define name="atomLanguageTag">
|
|
||||||
<data type="string">
|
|
||||||
<param name="pattern">[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*</param>
|
|
||||||
</data>
|
|
||||||
</define>
|
|
||||||
<!--
|
|
||||||
Unconstrained; it's not entirely clear how IRI fit into
|
|
||||||
xsd:anyURI so let's not try to constrain it here
|
|
||||||
-->
|
|
||||||
<define name="atomUri">
|
|
||||||
<text/>
|
|
||||||
</define>
|
|
||||||
<!-- Whatever an email address is, it contains at least one @ -->
|
|
||||||
<define name="atomEmailAddress">
|
|
||||||
<data type="string">
|
|
||||||
<param name="pattern">.+@.+</param>
|
|
||||||
</data>
|
|
||||||
</define>
|
|
||||||
<!-- Simple Extension -->
|
|
||||||
<define name="simpleExtensionElement">
|
|
||||||
<element>
|
|
||||||
<anyName>
|
|
||||||
<except>
|
|
||||||
<nsName ns="http://www.w3.org/2005/Atom"/>
|
|
||||||
</except>
|
|
||||||
</anyName>
|
|
||||||
<text/>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- Structured Extension -->
|
|
||||||
<define name="structuredExtensionElement">
|
|
||||||
<element>
|
|
||||||
<anyName>
|
|
||||||
<except>
|
|
||||||
<nsName ns="http://www.w3.org/2005/Atom"/>
|
|
||||||
</except>
|
|
||||||
</anyName>
|
|
||||||
<choice>
|
|
||||||
<group>
|
|
||||||
<oneOrMore>
|
|
||||||
<attribute>
|
|
||||||
<anyName/>
|
|
||||||
</attribute>
|
|
||||||
</oneOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</group>
|
|
||||||
<group>
|
|
||||||
<zeroOrMore>
|
|
||||||
<attribute>
|
|
||||||
<anyName/>
|
|
||||||
</attribute>
|
|
||||||
</zeroOrMore>
|
|
||||||
<group>
|
|
||||||
<optional>
|
|
||||||
<text/>
|
|
||||||
</optional>
|
|
||||||
<oneOrMore>
|
|
||||||
<ref name="anyElement"/>
|
|
||||||
</oneOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</choice>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- Other Extensibility -->
|
|
||||||
<define name="extensionElement">
|
|
||||||
<choice>
|
|
||||||
<ref name="simpleExtensionElement"/>
|
|
||||||
<ref name="structuredExtensionElement"/>
|
|
||||||
</choice>
|
|
||||||
</define>
|
|
||||||
<define name="undefinedAttribute">
|
|
||||||
<attribute>
|
|
||||||
<anyName>
|
|
||||||
<except>
|
|
||||||
<name>xml:base</name>
|
|
||||||
<name>xml:lang</name>
|
|
||||||
<nsName ns=""/>
|
|
||||||
</except>
|
|
||||||
</anyName>
|
|
||||||
</attribute>
|
|
||||||
</define>
|
|
||||||
<define name="undefinedContent">
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyForeignElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</define>
|
|
||||||
<define name="anyElement">
|
|
||||||
<element>
|
|
||||||
<anyName/>
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<attribute>
|
|
||||||
<anyName/>
|
|
||||||
</attribute>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<define name="anyForeignElement">
|
|
||||||
<element>
|
|
||||||
<anyName>
|
|
||||||
<except>
|
|
||||||
<nsName ns="http://www.w3.org/2005/Atom"/>
|
|
||||||
</except>
|
|
||||||
</anyName>
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<attribute>
|
|
||||||
<anyName/>
|
|
||||||
</attribute>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyElement"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<!-- XHTML -->
|
|
||||||
<define name="anyXHTML">
|
|
||||||
<element>
|
|
||||||
<nsName ns="http://www.w3.org/1999/xhtml"/>
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<attribute>
|
|
||||||
<anyName/>
|
|
||||||
</attribute>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyXHTML"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
<define name="xhtmlDiv">
|
|
||||||
<element name="xhtml:div">
|
|
||||||
<zeroOrMore>
|
|
||||||
<choice>
|
|
||||||
<attribute>
|
|
||||||
<anyName/>
|
|
||||||
</attribute>
|
|
||||||
<text/>
|
|
||||||
<ref name="anyXHTML"/>
|
|
||||||
</choice>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
</grammar>
|
|
|
@ -1,14 +0,0 @@
|
||||||
<element name="addresses" ns="http://docs.openstack.org/compute/api/v1.1"
|
|
||||||
xmlns="http://relaxng.org/ns/structure/1.0">
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="network">
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="ip">
|
|
||||||
<attribute name="version"> <text/> </attribute>
|
|
||||||
<attribute name="addr"> <text/> </attribute>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<element name="extension" ns="http://docs.openstack.org/common/api/v1.0"
|
|
||||||
xmlns="http://relaxng.org/ns/structure/1.0">
|
|
||||||
<attribute name="alias"> <text/> </attribute>
|
|
||||||
<attribute name="name"> <text/> </attribute>
|
|
||||||
<attribute name="namespace"> <text/> </attribute>
|
|
||||||
<attribute name="updated"> <text/> </attribute>
|
|
||||||
<element name="description"> <text/> </element>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<element name="extensions" xmlns="http://relaxng.org/ns/structure/1.0"
|
|
||||||
ns="http://docs.openstack.org/common/api/v1.0">
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="extension.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<element name="flavor" ns="http://docs.openstack.org/compute/api/v1.1"
|
|
||||||
xmlns="http://relaxng.org/ns/structure/1.0">
|
|
||||||
<attribute name="name"> <text/> </attribute>
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<attribute name="ram"> <text/> </attribute>
|
|
||||||
<attribute name="disk"> <text/> </attribute>
|
|
||||||
<attribute name="vcpus"> <text/> </attribute>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
|
@ -1,18 +0,0 @@
|
||||||
<element name="flavors" xmlns="http://relaxng.org/ns/structure/1.0"
|
|
||||||
ns="http://docs.openstack.org/compute/api/v1.1">
|
|
||||||
<choice>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="flavor.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<!-- flavors index -->
|
|
||||||
<element name="flavor">
|
|
||||||
<attribute name="name"> <text/> </attribute>
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</choice>
|
|
||||||
</element>
|
|
|
@ -1,36 +0,0 @@
|
||||||
<element name="image" ns="http://docs.openstack.org/compute/api/v1.1"
|
|
||||||
xmlns="http://relaxng.org/ns/structure/1.0">
|
|
||||||
<attribute name="name"> <text/> </attribute>
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<attribute name="updated"> <text/> </attribute>
|
|
||||||
<attribute name="created"> <text/> </attribute>
|
|
||||||
<attribute name="status"> <text/> </attribute>
|
|
||||||
<optional>
|
|
||||||
<attribute name="progress"> <text/> </attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="minDisk"> <text/> </attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="minRam"> <text/> </attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<element name="server">
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</optional>
|
|
||||||
<element name="metadata">
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="meta">
|
|
||||||
<attribute name="key"> <text/> </attribute>
|
|
||||||
<text/>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<element name="images" xmlns="http://relaxng.org/ns/structure/1.0"
|
|
||||||
ns="http://docs.openstack.org/compute/api/v1.1">
|
|
||||||
<choice>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="image.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<group>
|
|
||||||
<!-- images index -->
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="image">
|
|
||||||
<attribute name="name"> <text/> </attribute>
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</group>
|
|
||||||
</choice>
|
|
||||||
</element>
|
|
|
@ -1,28 +0,0 @@
|
||||||
<element name="limits" ns="http://docs.openstack.org/common/api/v1.0"
|
|
||||||
xmlns="http://relaxng.org/ns/structure/1.0">
|
|
||||||
<element name="rates">
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="rate">
|
|
||||||
<attribute name="uri"> <text/> </attribute>
|
|
||||||
<attribute name="regex"> <text/> </attribute>
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="limit">
|
|
||||||
<attribute name="value"> <text/> </attribute>
|
|
||||||
<attribute name="verb"> <text/> </attribute>
|
|
||||||
<attribute name="remaining"> <text/> </attribute>
|
|
||||||
<attribute name="unit"> <text/> </attribute>
|
|
||||||
<attribute name="next-available"> <text/> </attribute>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
<element name="absolute">
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="limit">
|
|
||||||
<attribute name="name"> <text/> </attribute>
|
|
||||||
<attribute name="value"> <text/> </attribute>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</element>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<element name="metadata" ns="http://docs.openstack.org/compute/api/v1.1"
|
|
||||||
xmlns="http://relaxng.org/ns/structure/1.0">
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="meta">
|
|
||||||
<attribute name="key"> <text/> </attribute>
|
|
||||||
<text/>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
|
@ -1,59 +0,0 @@
|
||||||
<element name="server" ns="http://docs.openstack.org/compute/api/v1.1"
|
|
||||||
xmlns="http://relaxng.org/ns/structure/1.0">
|
|
||||||
<attribute name="name"> <text/> </attribute>
|
|
||||||
<attribute name="userId"> <text/> </attribute>
|
|
||||||
<attribute name="tenantId"> <text/> </attribute>
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<attribute name="updated"> <text/> </attribute>
|
|
||||||
<attribute name="created"> <text/> </attribute>
|
|
||||||
<attribute name="hostId"> <text/> </attribute>
|
|
||||||
<attribute name="accessIPv4"> <text/> </attribute>
|
|
||||||
<attribute name="accessIPv6"> <text/> </attribute>
|
|
||||||
<attribute name="status"> <text/> </attribute>
|
|
||||||
<optional>
|
|
||||||
<attribute name="progress"> <text/> </attribute>
|
|
||||||
</optional>
|
|
||||||
<optional>
|
|
||||||
<attribute name="adminPass"><text/> </attribute>
|
|
||||||
</optional>
|
|
||||||
<element name="image">
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</element>
|
|
||||||
<element name="flavor">
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</element>
|
|
||||||
<optional>
|
|
||||||
<element name="fault">
|
|
||||||
<attribute name="code"> <text/> </attribute>
|
|
||||||
<attribute name="created"> <text/> </attribute>
|
|
||||||
<element name="message"> <text/> </element>
|
|
||||||
<element name="details"> <text/> </element>
|
|
||||||
</element>
|
|
||||||
</optional>
|
|
||||||
<element name="metadata">
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="meta">
|
|
||||||
<attribute name="key"> <text/> </attribute>
|
|
||||||
<text/>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
<element name="addresses">
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="network">
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="ip">
|
|
||||||
<attribute name="version"> <text/> </attribute>
|
|
||||||
<attribute name="addr"> <text/> </attribute>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<element name="servers" xmlns="http://relaxng.org/ns/structure/1.0"
|
|
||||||
ns="http://docs.openstack.org/compute/api/v1.1">
|
|
||||||
<choice>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="server.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
<group>
|
|
||||||
<!-- servers index -->
|
|
||||||
<zeroOrMore>
|
|
||||||
<element name="server">
|
|
||||||
<attribute name="name"> <text/> </attribute>
|
|
||||||
<attribute name="id"> <text/> </attribute>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
||||||
</zeroOrMore>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</group>
|
|
||||||
</choice>
|
|
||||||
</element>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<element name="version" ns="http://docs.openstack.org/common/api/v1.0"
|
|
||||||
xmlns="http://relaxng.org/ns/structure/1.0">
|
|
||||||
<attribute name="id"/>
|
|
||||||
<attribute name="status"/>
|
|
||||||
<attribute name="updated"/>
|
|
||||||
<element name="media-types">
|
|
||||||
<oneOrMore>
|
|
||||||
<element name="media-type">
|
|
||||||
<attribute name="base"/>
|
|
||||||
<attribute name="type"/>
|
|
||||||
</element>
|
|
||||||
</oneOrMore>
|
|
||||||
</element>
|
|
||||||
<zeroOrMore>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</zeroOrMore>
|
|
||||||
</element>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<element name="versions" xmlns="http://relaxng.org/ns/structure/1.0"
|
|
||||||
ns="http://docs.openstack.org/common/api/v1.0">
|
|
||||||
<oneOrMore>
|
|
||||||
<element name="version">
|
|
||||||
<attribute name="id"/>
|
|
||||||
<attribute name="status"/>
|
|
||||||
<attribute name="updated"/>
|
|
||||||
<externalRef href="../atom-link.rng"/>
|
|
||||||
</element>
|
|
||||||
</oneOrMore>
|
|
||||||
</element>
|
|
|
@ -42,14 +42,11 @@ class Controller(object):
|
||||||
meta_dict[key] = value
|
meta_dict[key] = value
|
||||||
return meta_dict
|
return meta_dict
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
"""Returns the list of metadata for a given instance."""
|
"""Returns the list of metadata for a given instance."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
return {'metadata': self._get_metadata(context, server_id)}
|
return {'metadata': self._get_metadata(context, server_id)}
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
|
||||||
@wsgi.deserializers(xml=common.MetadataDeserializer)
|
|
||||||
def create(self, req, server_id, body):
|
def create(self, req, server_id, body):
|
||||||
try:
|
try:
|
||||||
metadata = body['metadata']
|
metadata = body['metadata']
|
||||||
|
@ -69,8 +66,6 @@ class Controller(object):
|
||||||
|
|
||||||
return {'metadata': new_metadata}
|
return {'metadata': new_metadata}
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetaItemTemplate)
|
|
||||||
@wsgi.deserializers(xml=common.MetaItemDeserializer)
|
|
||||||
def update(self, req, server_id, id, body):
|
def update(self, req, server_id, id, body):
|
||||||
try:
|
try:
|
||||||
meta_item = body['meta']
|
meta_item = body['meta']
|
||||||
|
@ -98,8 +93,6 @@ class Controller(object):
|
||||||
|
|
||||||
return {'meta': meta_item}
|
return {'meta': meta_item}
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
|
||||||
@wsgi.deserializers(xml=common.MetadataDeserializer)
|
|
||||||
def update_all(self, req, server_id, body):
|
def update_all(self, req, server_id, body):
|
||||||
try:
|
try:
|
||||||
metadata = body['metadata']
|
metadata = body['metadata']
|
||||||
|
@ -154,7 +147,6 @@ class Controller(object):
|
||||||
common.raise_http_conflict_for_instance_invalid_state(state_error,
|
common.raise_http_conflict_for_instance_invalid_state(state_error,
|
||||||
'update metadata', server_id)
|
'update metadata', server_id)
|
||||||
|
|
||||||
@wsgi.serializers(xml=common.MetaItemTemplate)
|
|
||||||
def show(self, req, server_id, id):
|
def show(self, req, server_id, id):
|
||||||
"""Return a single metadata item."""
|
"""Return a single metadata item."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
|
@ -28,16 +28,13 @@ import webob
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack import common
|
from nova.api.openstack import common
|
||||||
from nova.api.openstack.compute import ips
|
|
||||||
from nova.api.openstack.compute.views import servers as views_servers
|
from nova.api.openstack.compute.views import servers as views_servers
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import block_device
|
from nova import block_device
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova.compute import flavors
|
from nova.compute import flavors
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.i18n import _LW
|
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
from nova.openstack.common import uuidutils
|
from nova.openstack.common import uuidutils
|
||||||
|
@ -64,421 +61,7 @@ LOG = logging.getLogger(__name__)
|
||||||
XML_WARNING = False
|
XML_WARNING = False
|
||||||
|
|
||||||
|
|
||||||
def make_fault(elem):
|
server_nsmap = {}
|
||||||
fault = xmlutil.SubTemplateElement(elem, 'fault', selector='fault')
|
|
||||||
fault.set('code')
|
|
||||||
fault.set('created')
|
|
||||||
msg = xmlutil.SubTemplateElement(fault, 'message')
|
|
||||||
msg.text = 'message'
|
|
||||||
det = xmlutil.SubTemplateElement(fault, 'details')
|
|
||||||
det.text = 'details'
|
|
||||||
|
|
||||||
|
|
||||||
def make_server(elem, detailed=False):
|
|
||||||
elem.set('name')
|
|
||||||
elem.set('id')
|
|
||||||
|
|
||||||
global XML_WARNING
|
|
||||||
if not XML_WARNING:
|
|
||||||
LOG.warning(_LW('XML support has been deprecated and may be removed '
|
|
||||||
'as early as the Juno release.'))
|
|
||||||
XML_WARNING = True
|
|
||||||
|
|
||||||
if detailed:
|
|
||||||
elem.set('userId', 'user_id')
|
|
||||||
elem.set('tenantId', 'tenant_id')
|
|
||||||
elem.set('updated')
|
|
||||||
elem.set('created')
|
|
||||||
elem.set('hostId')
|
|
||||||
elem.set('accessIPv4')
|
|
||||||
elem.set('accessIPv6')
|
|
||||||
elem.set('status')
|
|
||||||
elem.set('progress')
|
|
||||||
elem.set('reservation_id')
|
|
||||||
|
|
||||||
# Attach image node
|
|
||||||
image = xmlutil.SubTemplateElement(elem, 'image', selector='image')
|
|
||||||
image.set('id')
|
|
||||||
xmlutil.make_links(image, 'links')
|
|
||||||
|
|
||||||
# Attach flavor node
|
|
||||||
flavor = xmlutil.SubTemplateElement(elem, 'flavor', selector='flavor')
|
|
||||||
flavor.set('id')
|
|
||||||
xmlutil.make_links(flavor, 'links')
|
|
||||||
|
|
||||||
# Attach fault node
|
|
||||||
make_fault(elem)
|
|
||||||
|
|
||||||
# Attach metadata node
|
|
||||||
elem.append(common.MetadataTemplate())
|
|
||||||
|
|
||||||
# Attach addresses node
|
|
||||||
elem.append(ips.AddressesTemplate())
|
|
||||||
|
|
||||||
xmlutil.make_links(elem, 'links')
|
|
||||||
|
|
||||||
|
|
||||||
server_nsmap = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
|
|
||||||
|
|
||||||
|
|
||||||
class ServerTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server', selector='server')
|
|
||||||
make_server(root, detailed=True)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=server_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class MinimalServersTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem)
|
|
||||||
xmlutil.make_links(root, 'servers_links')
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=server_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ServersTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('servers')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
|
|
||||||
make_server(elem, detailed=True)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=server_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerAdminPassTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server')
|
|
||||||
root.set('adminPass')
|
|
||||||
return xmlutil.SlaveTemplate(root, 1, nsmap=server_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ServerMultipleCreateTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('server')
|
|
||||||
root.set('reservation_id')
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=server_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
def FullServerTemplate():
|
|
||||||
master = ServerTemplate()
|
|
||||||
master.attach(ServerAdminPassTemplate())
|
|
||||||
return master
|
|
||||||
|
|
||||||
|
|
||||||
class CommonDeserializer(wsgi.MetadataXMLDeserializer):
|
|
||||||
"""Common deserializer to handle xml-formatted server create requests.
|
|
||||||
|
|
||||||
Handles standard server attributes as well as optional metadata
|
|
||||||
and personality attributes
|
|
||||||
"""
|
|
||||||
|
|
||||||
metadata_deserializer = common.MetadataXMLDeserializer()
|
|
||||||
|
|
||||||
def _extract_personality(self, server_node):
|
|
||||||
"""Marshal the personality attribute of a parsed request."""
|
|
||||||
node = self.find_first_child_named(server_node, "personality")
|
|
||||||
if node is not None:
|
|
||||||
personality = []
|
|
||||||
for file_node in self.find_children_named(node, "file"):
|
|
||||||
item = {}
|
|
||||||
if file_node.hasAttribute("path"):
|
|
||||||
item["path"] = file_node.getAttribute("path")
|
|
||||||
item["contents"] = self.extract_text(file_node)
|
|
||||||
personality.append(item)
|
|
||||||
return personality
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _extract_server(self, node):
|
|
||||||
"""Marshal the server attribute of a parsed request."""
|
|
||||||
server = {}
|
|
||||||
server_node = self.find_first_child_named(node, 'server')
|
|
||||||
|
|
||||||
attributes = ["name", "imageRef", "flavorRef", "adminPass",
|
|
||||||
"accessIPv4", "accessIPv6", "key_name",
|
|
||||||
"availability_zone", "min_count", "max_count"]
|
|
||||||
for attr in attributes:
|
|
||||||
if server_node.getAttribute(attr):
|
|
||||||
server[attr] = server_node.getAttribute(attr)
|
|
||||||
|
|
||||||
res_id = server_node.getAttribute('return_reservation_id')
|
|
||||||
if res_id:
|
|
||||||
server['return_reservation_id'] = \
|
|
||||||
strutils.bool_from_string(res_id)
|
|
||||||
|
|
||||||
scheduler_hints = self._extract_scheduler_hints(server_node)
|
|
||||||
if scheduler_hints:
|
|
||||||
server['OS-SCH-HNT:scheduler_hints'] = scheduler_hints
|
|
||||||
|
|
||||||
metadata_node = self.find_first_child_named(server_node, "metadata")
|
|
||||||
if metadata_node is not None:
|
|
||||||
server["metadata"] = self.extract_metadata(metadata_node)
|
|
||||||
|
|
||||||
user_data_node = self.find_first_child_named(server_node, "user_data")
|
|
||||||
if user_data_node is not None:
|
|
||||||
server["user_data"] = self.extract_text(user_data_node)
|
|
||||||
|
|
||||||
personality = self._extract_personality(server_node)
|
|
||||||
if personality is not None:
|
|
||||||
server["personality"] = personality
|
|
||||||
|
|
||||||
networks = self._extract_networks(server_node)
|
|
||||||
if networks is not None:
|
|
||||||
server["networks"] = networks
|
|
||||||
|
|
||||||
security_groups = self._extract_security_groups(server_node)
|
|
||||||
if security_groups is not None:
|
|
||||||
server["security_groups"] = security_groups
|
|
||||||
|
|
||||||
# NOTE(vish): this is not namespaced in json, so leave it without a
|
|
||||||
# namespace for now
|
|
||||||
block_device_mapping = self._extract_block_device_mapping(server_node)
|
|
||||||
if block_device_mapping is not None:
|
|
||||||
server["block_device_mapping"] = block_device_mapping
|
|
||||||
|
|
||||||
block_device_mapping_v2 = self._extract_block_device_mapping_v2(
|
|
||||||
server_node)
|
|
||||||
if block_device_mapping_v2 is not None:
|
|
||||||
server["block_device_mapping_v2"] = block_device_mapping_v2
|
|
||||||
|
|
||||||
# NOTE(vish): Support this incorrect version because it was in the code
|
|
||||||
# base for a while and we don't want to accidentally break
|
|
||||||
# anyone that might be using it.
|
|
||||||
auto_disk_config = server_node.getAttribute('auto_disk_config')
|
|
||||||
if auto_disk_config:
|
|
||||||
server['OS-DCF:diskConfig'] = auto_disk_config
|
|
||||||
|
|
||||||
auto_disk_config = server_node.getAttribute('OS-DCF:diskConfig')
|
|
||||||
if auto_disk_config:
|
|
||||||
server['OS-DCF:diskConfig'] = auto_disk_config
|
|
||||||
|
|
||||||
config_drive = server_node.getAttribute('config_drive')
|
|
||||||
if config_drive:
|
|
||||||
server['config_drive'] = config_drive
|
|
||||||
|
|
||||||
return server
|
|
||||||
|
|
||||||
def _extract_block_device_mapping(self, server_node):
|
|
||||||
"""Marshal the block_device_mapping node of a parsed request."""
|
|
||||||
node = self.find_first_child_named(server_node, "block_device_mapping")
|
|
||||||
if node:
|
|
||||||
block_device_mapping = []
|
|
||||||
for child in self.extract_elements(node):
|
|
||||||
if child.nodeName != "mapping":
|
|
||||||
continue
|
|
||||||
mapping = {}
|
|
||||||
attributes = ["volume_id", "snapshot_id", "device_name",
|
|
||||||
"virtual_name", "volume_size"]
|
|
||||||
for attr in attributes:
|
|
||||||
value = child.getAttribute(attr)
|
|
||||||
if value:
|
|
||||||
mapping[attr] = value
|
|
||||||
attributes = ["delete_on_termination", "no_device"]
|
|
||||||
for attr in attributes:
|
|
||||||
value = child.getAttribute(attr)
|
|
||||||
if value:
|
|
||||||
mapping[attr] = strutils.bool_from_string(value)
|
|
||||||
block_device_mapping.append(mapping)
|
|
||||||
return block_device_mapping
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _extract_block_device_mapping_v2(self, server_node):
|
|
||||||
"""Marshal the new block_device_mappings."""
|
|
||||||
node = self.find_first_child_named(server_node,
|
|
||||||
"block_device_mapping_v2")
|
|
||||||
if node:
|
|
||||||
block_device_mapping = []
|
|
||||||
for child in self.extract_elements(node):
|
|
||||||
if child.nodeName != "mapping":
|
|
||||||
continue
|
|
||||||
block_device_mapping.append(
|
|
||||||
dict((attr, child.getAttribute(attr))
|
|
||||||
for attr in block_device.bdm_new_api_fields
|
|
||||||
if child.getAttribute(attr)))
|
|
||||||
return block_device_mapping
|
|
||||||
|
|
||||||
def _extract_scheduler_hints(self, server_node):
|
|
||||||
"""Marshal the scheduler hints attribute of a parsed request."""
|
|
||||||
node = self.find_first_child_named_in_namespace(server_node,
|
|
||||||
"http://docs.openstack.org/compute/ext/scheduler-hints/api/v2",
|
|
||||||
"scheduler_hints")
|
|
||||||
if node:
|
|
||||||
scheduler_hints = {}
|
|
||||||
for child in self.extract_elements(node):
|
|
||||||
scheduler_hints.setdefault(child.nodeName, [])
|
|
||||||
value = self.extract_text(child).strip()
|
|
||||||
scheduler_hints[child.nodeName].append(value)
|
|
||||||
return scheduler_hints
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _extract_networks(self, server_node):
|
|
||||||
"""Marshal the networks attribute of a parsed request."""
|
|
||||||
node = self.find_first_child_named(server_node, "networks")
|
|
||||||
if node is not None:
|
|
||||||
networks = []
|
|
||||||
for network_node in self.find_children_named(node,
|
|
||||||
"network"):
|
|
||||||
item = {}
|
|
||||||
if network_node.hasAttribute("uuid"):
|
|
||||||
item["uuid"] = network_node.getAttribute("uuid")
|
|
||||||
if network_node.hasAttribute("fixed_ip"):
|
|
||||||
item["fixed_ip"] = network_node.getAttribute("fixed_ip")
|
|
||||||
if network_node.hasAttribute("port"):
|
|
||||||
item["port"] = network_node.getAttribute("port")
|
|
||||||
networks.append(item)
|
|
||||||
return networks
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _extract_security_groups(self, server_node):
|
|
||||||
"""Marshal the security_groups attribute of a parsed request."""
|
|
||||||
node = self.find_first_child_named(server_node, "security_groups")
|
|
||||||
if node is not None:
|
|
||||||
security_groups = []
|
|
||||||
for sg_node in self.find_children_named(node, "security_group"):
|
|
||||||
item = {}
|
|
||||||
name = self.find_attribute_or_element(sg_node, 'name')
|
|
||||||
if name:
|
|
||||||
item["name"] = name
|
|
||||||
security_groups.append(item)
|
|
||||||
return security_groups
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class ActionDeserializer(CommonDeserializer):
|
|
||||||
"""Deserializer to handle xml-formatted server action requests.
|
|
||||||
|
|
||||||
Handles standard server attributes as well as optional metadata
|
|
||||||
and personality attributes
|
|
||||||
"""
|
|
||||||
|
|
||||||
def default(self, string):
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
action_node = dom.childNodes[0]
|
|
||||||
action_name = action_node.tagName
|
|
||||||
|
|
||||||
action_deserializer = {
|
|
||||||
'createImage': self._action_create_image,
|
|
||||||
'changePassword': self._action_change_password,
|
|
||||||
'reboot': self._action_reboot,
|
|
||||||
'rebuild': self._action_rebuild,
|
|
||||||
'resize': self._action_resize,
|
|
||||||
'confirmResize': self._action_confirm_resize,
|
|
||||||
'revertResize': self._action_revert_resize,
|
|
||||||
}.get(action_name, super(ActionDeserializer, self).default)
|
|
||||||
|
|
||||||
action_data = action_deserializer(action_node)
|
|
||||||
|
|
||||||
return {'body': {action_name: action_data}}
|
|
||||||
|
|
||||||
def _action_create_image(self, node):
|
|
||||||
return self._deserialize_image_action(node, ('name',))
|
|
||||||
|
|
||||||
def _action_change_password(self, node):
|
|
||||||
if not node.hasAttribute("adminPass"):
|
|
||||||
raise AttributeError("No adminPass was specified in request")
|
|
||||||
return {"adminPass": node.getAttribute("adminPass")}
|
|
||||||
|
|
||||||
def _action_reboot(self, node):
|
|
||||||
if not node.hasAttribute("type"):
|
|
||||||
raise AttributeError("No reboot type was specified in request")
|
|
||||||
return {"type": node.getAttribute("type")}
|
|
||||||
|
|
||||||
def _action_rebuild(self, node):
|
|
||||||
rebuild = {}
|
|
||||||
if node.hasAttribute("name"):
|
|
||||||
name = node.getAttribute("name")
|
|
||||||
if not name:
|
|
||||||
raise AttributeError("Name cannot be blank")
|
|
||||||
rebuild['name'] = name
|
|
||||||
|
|
||||||
if node.hasAttribute("auto_disk_config"):
|
|
||||||
rebuild['OS-DCF:diskConfig'] = node.getAttribute(
|
|
||||||
"auto_disk_config")
|
|
||||||
|
|
||||||
if node.hasAttribute("OS-DCF:diskConfig"):
|
|
||||||
rebuild['OS-DCF:diskConfig'] = node.getAttribute(
|
|
||||||
"OS-DCF:diskConfig")
|
|
||||||
|
|
||||||
metadata_node = self.find_first_child_named(node, "metadata")
|
|
||||||
if metadata_node is not None:
|
|
||||||
rebuild["metadata"] = self.extract_metadata(metadata_node)
|
|
||||||
|
|
||||||
personality = self._extract_personality(node)
|
|
||||||
if personality is not None:
|
|
||||||
rebuild["personality"] = personality
|
|
||||||
|
|
||||||
if not node.hasAttribute("imageRef"):
|
|
||||||
raise AttributeError("No imageRef was specified in request")
|
|
||||||
rebuild["imageRef"] = node.getAttribute("imageRef")
|
|
||||||
|
|
||||||
if node.hasAttribute("adminPass"):
|
|
||||||
rebuild["adminPass"] = node.getAttribute("adminPass")
|
|
||||||
|
|
||||||
if node.hasAttribute("accessIPv4"):
|
|
||||||
rebuild["accessIPv4"] = node.getAttribute("accessIPv4")
|
|
||||||
|
|
||||||
if node.hasAttribute("accessIPv6"):
|
|
||||||
rebuild["accessIPv6"] = node.getAttribute("accessIPv6")
|
|
||||||
|
|
||||||
if node.hasAttribute("preserve_ephemeral"):
|
|
||||||
rebuild["preserve_ephemeral"] = strutils.bool_from_string(
|
|
||||||
node.getAttribute("preserve_ephemeral"), strict=True)
|
|
||||||
|
|
||||||
return rebuild
|
|
||||||
|
|
||||||
def _action_resize(self, node):
|
|
||||||
resize = {}
|
|
||||||
|
|
||||||
if node.hasAttribute("flavorRef"):
|
|
||||||
resize["flavorRef"] = node.getAttribute("flavorRef")
|
|
||||||
else:
|
|
||||||
raise AttributeError("No flavorRef was specified in request")
|
|
||||||
|
|
||||||
if node.hasAttribute("auto_disk_config"):
|
|
||||||
resize['OS-DCF:diskConfig'] = node.getAttribute("auto_disk_config")
|
|
||||||
|
|
||||||
if node.hasAttribute("OS-DCF:diskConfig"):
|
|
||||||
resize['OS-DCF:diskConfig'] = node.getAttribute(
|
|
||||||
"OS-DCF:diskConfig")
|
|
||||||
|
|
||||||
return resize
|
|
||||||
|
|
||||||
def _action_confirm_resize(self, node):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _action_revert_resize(self, node):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _deserialize_image_action(self, node, allowed_attributes):
|
|
||||||
data = {}
|
|
||||||
for attribute in allowed_attributes:
|
|
||||||
value = node.getAttribute(attribute)
|
|
||||||
if value:
|
|
||||||
data[attribute] = value
|
|
||||||
metadata_node = self.find_first_child_named(node, 'metadata')
|
|
||||||
if metadata_node is not None:
|
|
||||||
metadata = self.metadata_deserializer.extract_metadata(
|
|
||||||
metadata_node)
|
|
||||||
data['metadata'] = metadata
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class CreateDeserializer(CommonDeserializer):
|
|
||||||
"""Deserializer to handle xml-formatted server create requests.
|
|
||||||
|
|
||||||
Handles standard server attributes as well as optional metadata
|
|
||||||
and personality attributes
|
|
||||||
"""
|
|
||||||
|
|
||||||
def default(self, string):
|
|
||||||
"""Deserialize an xml-formatted server create request."""
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(string)
|
|
||||||
server = self._extract_server(dom)
|
|
||||||
return {'body': {'server': server}}
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
|
@ -505,7 +88,6 @@ class Controller(wsgi.Controller):
|
||||||
self.compute_api = compute.API()
|
self.compute_api = compute.API()
|
||||||
self.ext_mgr = ext_mgr
|
self.ext_mgr = ext_mgr
|
||||||
|
|
||||||
@wsgi.serializers(xml=MinimalServersTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Returns a list of server names and ids for a given user."""
|
"""Returns a list of server names and ids for a given user."""
|
||||||
try:
|
try:
|
||||||
|
@ -514,7 +96,6 @@ class Controller(wsgi.Controller):
|
||||||
raise exc.HTTPBadRequest(explanation=err.format_message())
|
raise exc.HTTPBadRequest(explanation=err.format_message())
|
||||||
return servers
|
return servers
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServersTemplate)
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Returns a list of server details for a given user."""
|
"""Returns a list of server details for a given user."""
|
||||||
try:
|
try:
|
||||||
|
@ -760,7 +341,6 @@ class Controller(wsgi.Controller):
|
||||||
expl = _('accessIPv6 is not proper IPv6 format')
|
expl = _('accessIPv6 is not proper IPv6 format')
|
||||||
raise exc.HTTPBadRequest(explanation=expl)
|
raise exc.HTTPBadRequest(explanation=expl)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServerTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Returns server details by server id."""
|
"""Returns server details by server id."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -768,8 +348,6 @@ class Controller(wsgi.Controller):
|
||||||
return self._view_builder.show(req, instance)
|
return self._view_builder.show(req, instance)
|
||||||
|
|
||||||
@wsgi.response(202)
|
@wsgi.response(202)
|
||||||
@wsgi.serializers(xml=FullServerTemplate)
|
|
||||||
@wsgi.deserializers(xml=CreateDeserializer)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Creates a new server for a given user."""
|
"""Creates a new server for a given user."""
|
||||||
if not self.is_valid_body(body, 'server'):
|
if not self.is_valid_body(body, 'server'):
|
||||||
|
@ -1013,8 +591,7 @@ class Controller(wsgi.Controller):
|
||||||
|
|
||||||
# If the caller wanted a reservation_id, return it
|
# If the caller wanted a reservation_id, return it
|
||||||
if ret_resv_id:
|
if ret_resv_id:
|
||||||
return wsgi.ResponseObject({'reservation_id': resv_id},
|
return wsgi.ResponseObject({'reservation_id': resv_id})
|
||||||
xml=ServerMultipleCreateTemplate)
|
|
||||||
|
|
||||||
req.cache_db_instances(instances)
|
req.cache_db_instances(instances)
|
||||||
server = self._view_builder.create(req, instances[0])
|
server = self._view_builder.create(req, instances[0])
|
||||||
|
@ -1039,7 +616,6 @@ class Controller(wsgi.Controller):
|
||||||
else:
|
else:
|
||||||
self.compute_api.delete(context, instance)
|
self.compute_api.delete(context, instance)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ServerTemplate)
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
"""Update server then pass on to version-specific controller."""
|
"""Update server then pass on to version-specific controller."""
|
||||||
if not self.is_valid_body(body, 'server'):
|
if not self.is_valid_body(body, 'server'):
|
||||||
|
@ -1093,8 +669,6 @@ class Controller(wsgi.Controller):
|
||||||
return self._view_builder.show(req, instance)
|
return self._view_builder.show(req, instance)
|
||||||
|
|
||||||
@wsgi.response(204)
|
@wsgi.response(204)
|
||||||
@wsgi.serializers(xml=FullServerTemplate)
|
|
||||||
@wsgi.deserializers(xml=ActionDeserializer)
|
|
||||||
@wsgi.action('confirmResize')
|
@wsgi.action('confirmResize')
|
||||||
def _action_confirm_resize(self, req, id, body):
|
def _action_confirm_resize(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -1111,8 +685,6 @@ class Controller(wsgi.Controller):
|
||||||
'confirmResize', id)
|
'confirmResize', id)
|
||||||
|
|
||||||
@wsgi.response(202)
|
@wsgi.response(202)
|
||||||
@wsgi.serializers(xml=FullServerTemplate)
|
|
||||||
@wsgi.deserializers(xml=ActionDeserializer)
|
|
||||||
@wsgi.action('revertResize')
|
@wsgi.action('revertResize')
|
||||||
def _action_revert_resize(self, req, id, body):
|
def _action_revert_resize(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -1133,8 +705,6 @@ class Controller(wsgi.Controller):
|
||||||
return webob.Response(status_int=202)
|
return webob.Response(status_int=202)
|
||||||
|
|
||||||
@wsgi.response(202)
|
@wsgi.response(202)
|
||||||
@wsgi.serializers(xml=FullServerTemplate)
|
|
||||||
@wsgi.deserializers(xml=ActionDeserializer)
|
|
||||||
@wsgi.action('reboot')
|
@wsgi.action('reboot')
|
||||||
def _action_reboot(self, req, id, body):
|
def _action_reboot(self, req, id, body):
|
||||||
if 'reboot' in body and 'type' in body['reboot']:
|
if 'reboot' in body and 'type' in body['reboot']:
|
||||||
|
@ -1272,8 +842,6 @@ class Controller(wsgi.Controller):
|
||||||
return common.get_id_from_href(flavor_ref)
|
return common.get_id_from_href(flavor_ref)
|
||||||
|
|
||||||
@wsgi.response(202)
|
@wsgi.response(202)
|
||||||
@wsgi.serializers(xml=FullServerTemplate)
|
|
||||||
@wsgi.deserializers(xml=ActionDeserializer)
|
|
||||||
@wsgi.action('changePassword')
|
@wsgi.action('changePassword')
|
||||||
def _action_change_password(self, req, id, body):
|
def _action_change_password(self, req, id, body):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
@ -1301,8 +869,6 @@ class Controller(wsgi.Controller):
|
||||||
raise exc.HTTPBadRequest(explanation=msg)
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
@wsgi.response(202)
|
@wsgi.response(202)
|
||||||
@wsgi.serializers(xml=FullServerTemplate)
|
|
||||||
@wsgi.deserializers(xml=ActionDeserializer)
|
|
||||||
@wsgi.action('resize')
|
@wsgi.action('resize')
|
||||||
def _action_resize(self, req, id, body):
|
def _action_resize(self, req, id, body):
|
||||||
"""Resizes a given instance to the flavor size requested."""
|
"""Resizes a given instance to the flavor size requested."""
|
||||||
|
@ -1322,8 +888,6 @@ class Controller(wsgi.Controller):
|
||||||
return self._resize(req, id, flavor_ref, **kwargs)
|
return self._resize(req, id, flavor_ref, **kwargs)
|
||||||
|
|
||||||
@wsgi.response(202)
|
@wsgi.response(202)
|
||||||
@wsgi.serializers(xml=FullServerTemplate)
|
|
||||||
@wsgi.deserializers(xml=ActionDeserializer)
|
|
||||||
@wsgi.action('rebuild')
|
@wsgi.action('rebuild')
|
||||||
def _action_rebuild(self, req, id, body):
|
def _action_rebuild(self, req, id, body):
|
||||||
"""Rebuild an instance with the given attributes."""
|
"""Rebuild an instance with the given attributes."""
|
||||||
|
@ -1426,8 +990,6 @@ class Controller(wsgi.Controller):
|
||||||
return self._add_location(robj)
|
return self._add_location(robj)
|
||||||
|
|
||||||
@wsgi.response(202)
|
@wsgi.response(202)
|
||||||
@wsgi.serializers(xml=FullServerTemplate)
|
|
||||||
@wsgi.deserializers(xml=ActionDeserializer)
|
|
||||||
@wsgi.action('createImage')
|
@wsgi.action('createImage')
|
||||||
@common.check_snapshots_enabled
|
@common.check_snapshots_enabled
|
||||||
def _action_create_image(self, req, id, body):
|
def _action_create_image(self, req, id, body):
|
||||||
|
|
|
@ -13,13 +13,10 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
from oslo.utils import timeutils
|
|
||||||
|
|
||||||
from nova.api.openstack.compute.views import versions as views_versions
|
from nova.api.openstack.compute.views import versions as views_versions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
@ -48,10 +45,6 @@ VERSIONS = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"media-types": [
|
"media-types": [
|
||||||
{
|
|
||||||
"base": "application/xml",
|
|
||||||
"type": "application/vnd.openstack.compute+xml;version=2",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"base": "application/json",
|
"base": "application/json",
|
||||||
"type": "application/vnd.openstack.compute+json;version=2",
|
"type": "application/vnd.openstack.compute+json;version=2",
|
||||||
|
@ -79,142 +72,7 @@ VERSIONS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class MediaTypesTemplateElement(xmlutil.TemplateElement):
|
version_nsmap = {}
|
||||||
def will_render(self, datum):
|
|
||||||
return 'media-types' in datum
|
|
||||||
|
|
||||||
|
|
||||||
def make_version(elem):
|
|
||||||
elem.set('id')
|
|
||||||
elem.set('status')
|
|
||||||
elem.set('updated')
|
|
||||||
|
|
||||||
mts = MediaTypesTemplateElement('media-types')
|
|
||||||
elem.append(mts)
|
|
||||||
|
|
||||||
mt = xmlutil.SubTemplateElement(mts, 'media-type', selector='media-types')
|
|
||||||
mt.set('base')
|
|
||||||
mt.set('type')
|
|
||||||
|
|
||||||
xmlutil.make_links(elem, 'links')
|
|
||||||
|
|
||||||
|
|
||||||
version_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
|
|
||||||
|
|
||||||
|
|
||||||
class VersionTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('version', selector='version')
|
|
||||||
make_version(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class VersionsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('versions')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'version', selector='versions')
|
|
||||||
make_version(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ChoicesTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('choices')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'version', selector='choices')
|
|
||||||
make_version(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class AtomSerializer(wsgi.XMLDictSerializer):
|
|
||||||
|
|
||||||
NSMAP = {None: xmlutil.XMLNS_ATOM}
|
|
||||||
|
|
||||||
def __init__(self, metadata=None, xmlns=None):
|
|
||||||
self.metadata = metadata or {}
|
|
||||||
if not xmlns:
|
|
||||||
self.xmlns = wsgi.XMLNS_ATOM
|
|
||||||
else:
|
|
||||||
self.xmlns = xmlns
|
|
||||||
|
|
||||||
def _get_most_recent_update(self, versions):
|
|
||||||
recent = None
|
|
||||||
for version in versions:
|
|
||||||
updated = timeutils.parse_strtime(version['updated'],
|
|
||||||
'%Y-%m-%dT%H:%M:%SZ')
|
|
||||||
if not recent:
|
|
||||||
recent = updated
|
|
||||||
elif updated > recent:
|
|
||||||
recent = updated
|
|
||||||
|
|
||||||
return recent.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
||||||
|
|
||||||
def _get_base_url(self, link_href):
|
|
||||||
# Make sure no trailing /
|
|
||||||
link_href = link_href.rstrip('/')
|
|
||||||
return link_href.rsplit('/', 1)[0] + '/'
|
|
||||||
|
|
||||||
def _create_feed(self, versions, feed_title, feed_id):
|
|
||||||
feed = etree.Element('feed', nsmap=self.NSMAP)
|
|
||||||
title = etree.SubElement(feed, 'title')
|
|
||||||
title.set('type', 'text')
|
|
||||||
title.text = feed_title
|
|
||||||
|
|
||||||
# Set this updated to the most recently updated version
|
|
||||||
recent = self._get_most_recent_update(versions)
|
|
||||||
etree.SubElement(feed, 'updated').text = recent
|
|
||||||
|
|
||||||
etree.SubElement(feed, 'id').text = feed_id
|
|
||||||
|
|
||||||
link = etree.SubElement(feed, 'link')
|
|
||||||
link.set('rel', 'self')
|
|
||||||
link.set('href', feed_id)
|
|
||||||
|
|
||||||
author = etree.SubElement(feed, 'author')
|
|
||||||
etree.SubElement(author, 'name').text = 'Rackspace'
|
|
||||||
etree.SubElement(author, 'uri').text = 'http://www.rackspace.com/'
|
|
||||||
|
|
||||||
for version in versions:
|
|
||||||
feed.append(self._create_version_entry(version))
|
|
||||||
|
|
||||||
return feed
|
|
||||||
|
|
||||||
def _create_version_entry(self, version):
|
|
||||||
entry = etree.Element('entry')
|
|
||||||
etree.SubElement(entry, 'id').text = version['links'][0]['href']
|
|
||||||
title = etree.SubElement(entry, 'title')
|
|
||||||
title.set('type', 'text')
|
|
||||||
title.text = 'Version %s' % version['id']
|
|
||||||
etree.SubElement(entry, 'updated').text = version['updated']
|
|
||||||
|
|
||||||
for link in version['links']:
|
|
||||||
link_elem = etree.SubElement(entry, 'link')
|
|
||||||
link_elem.set('rel', link['rel'])
|
|
||||||
link_elem.set('href', link['href'])
|
|
||||||
if 'type' in link:
|
|
||||||
link_elem.set('type', link['type'])
|
|
||||||
|
|
||||||
content = etree.SubElement(entry, 'content')
|
|
||||||
content.set('type', 'text')
|
|
||||||
content.text = 'Version %s %s (%s)' % (version['id'],
|
|
||||||
version['status'],
|
|
||||||
version['updated'])
|
|
||||||
return entry
|
|
||||||
|
|
||||||
|
|
||||||
class VersionsAtomSerializer(AtomSerializer):
|
|
||||||
def default(self, data):
|
|
||||||
versions = data['versions']
|
|
||||||
feed_id = self._get_base_url(versions[0]['links'][0]['href'])
|
|
||||||
feed = self._create_feed(versions, 'Available API Versions', feed_id)
|
|
||||||
return self._to_xml(feed)
|
|
||||||
|
|
||||||
|
|
||||||
class VersionAtomSerializer(AtomSerializer):
|
|
||||||
def default(self, data):
|
|
||||||
version = data['version']
|
|
||||||
feed_id = version['links'][0]['href']
|
|
||||||
feed = self._create_feed([version], 'About This Version', feed_id)
|
|
||||||
return self._to_xml(feed)
|
|
||||||
|
|
||||||
|
|
||||||
class Versions(wsgi.Resource):
|
class Versions(wsgi.Resource):
|
||||||
|
@ -223,14 +81,11 @@ class Versions(wsgi.Resource):
|
||||||
if not CONF.osapi_v3.enabled:
|
if not CONF.osapi_v3.enabled:
|
||||||
del VERSIONS["v2.1"]
|
del VERSIONS["v2.1"]
|
||||||
|
|
||||||
@wsgi.serializers(xml=VersionsTemplate,
|
|
||||||
atom=VersionsAtomSerializer)
|
|
||||||
def index(self, req, body=None):
|
def index(self, req, body=None):
|
||||||
"""Return all versions."""
|
"""Return all versions."""
|
||||||
builder = views_versions.get_view_builder(req)
|
builder = views_versions.get_view_builder(req)
|
||||||
return builder.build_versions(VERSIONS)
|
return builder.build_versions(VERSIONS)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ChoicesTemplate)
|
|
||||||
@wsgi.response(300)
|
@wsgi.response(300)
|
||||||
def multi(self, req, body=None):
|
def multi(self, req, body=None):
|
||||||
"""Return multiple choices."""
|
"""Return multiple choices."""
|
||||||
|
@ -249,8 +104,6 @@ class Versions(wsgi.Resource):
|
||||||
|
|
||||||
|
|
||||||
class VersionV2(object):
|
class VersionV2(object):
|
||||||
@wsgi.serializers(xml=VersionTemplate,
|
|
||||||
atom=VersionAtomSerializer)
|
|
||||||
def show(self, req):
|
def show(self, req):
|
||||||
builder = views_versions.get_view_builder(req)
|
builder = views_versions.get_view_builder(req)
|
||||||
return builder.build_version(VERSIONS['v2.0'])
|
return builder.build_version(VERSIONS['v2.0'])
|
||||||
|
|
|
@ -25,7 +25,6 @@ import webob.exc
|
||||||
|
|
||||||
import nova.api.openstack
|
import nova.api.openstack
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.i18n import _LE
|
from nova.i18n import _LE
|
||||||
|
@ -95,42 +94,8 @@ class ExtensionDescriptor(object):
|
||||||
|
|
||||||
return nsmap
|
return nsmap
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def xmlname(cls, name):
|
|
||||||
"""Synthesize element and attribute names."""
|
|
||||||
|
|
||||||
return '{%s}%s' % (cls.namespace, name)
|
ext_nsmap = {}
|
||||||
|
|
||||||
|
|
||||||
def make_ext(elem):
|
|
||||||
elem.set('name')
|
|
||||||
elem.set('namespace')
|
|
||||||
elem.set('alias')
|
|
||||||
elem.set('updated')
|
|
||||||
|
|
||||||
desc = xmlutil.SubTemplateElement(elem, 'description')
|
|
||||||
desc.text = 'description'
|
|
||||||
|
|
||||||
xmlutil.make_links(elem, 'links')
|
|
||||||
|
|
||||||
|
|
||||||
ext_nsmap = {None: xmlutil.XMLNS_COMMON_V10, 'atom': xmlutil.XMLNS_ATOM}
|
|
||||||
|
|
||||||
|
|
||||||
class ExtensionTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('extension', selector='extension')
|
|
||||||
make_ext(root)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=ext_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtensionsTemplate(xmlutil.TemplateBuilder):
|
|
||||||
def construct(self):
|
|
||||||
root = xmlutil.TemplateElement('extensions')
|
|
||||||
elem = xmlutil.SubTemplateElement(root, 'extension',
|
|
||||||
selector='extensions')
|
|
||||||
make_ext(elem)
|
|
||||||
return xmlutil.MasterTemplate(root, 1, nsmap=ext_nsmap)
|
|
||||||
|
|
||||||
|
|
||||||
class ExtensionsController(wsgi.Resource):
|
class ExtensionsController(wsgi.Resource):
|
||||||
|
@ -149,14 +114,12 @@ class ExtensionsController(wsgi.Resource):
|
||||||
ext_data['links'] = [] # TODO(dprince): implement extension links
|
ext_data['links'] = [] # TODO(dprince): implement extension links
|
||||||
return ext_data
|
return ext_data
|
||||||
|
|
||||||
@wsgi.serializers(xml=ExtensionsTemplate)
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
extensions = []
|
extensions = []
|
||||||
for ext in self.extension_manager.sorted_extensions():
|
for ext in self.extension_manager.sorted_extensions():
|
||||||
extensions.append(self._translate(ext))
|
extensions.append(self._translate(ext))
|
||||||
return dict(extensions=extensions)
|
return dict(extensions=extensions)
|
||||||
|
|
||||||
@wsgi.serializers(xml=ExtensionTemplate)
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
try:
|
try:
|
||||||
# NOTE(dprince): the extensions alias is used as the 'id' for show
|
# NOTE(dprince): the extensions alias is used as the 'id' for show
|
||||||
|
|
|
@ -18,9 +18,7 @@ import functools
|
||||||
import inspect
|
import inspect
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
from xml.dom import minidom
|
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
from oslo.serialization import jsonutils
|
from oslo.serialization import jsonutils
|
||||||
from oslo.utils import strutils
|
from oslo.utils import strutils
|
||||||
import six
|
import six
|
||||||
|
@ -28,7 +26,6 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import api_version_request as api_version
|
from nova.api.openstack import api_version_request as api_version
|
||||||
from nova.api.openstack import versioned_method
|
from nova.api.openstack import versioned_method
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import i18n
|
from nova import i18n
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
@ -42,8 +39,6 @@ from nova import wsgi
|
||||||
XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
|
XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
|
||||||
XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
|
XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
|
||||||
|
|
||||||
XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
_SUPPORTED_CONTENT_TYPES = (
|
_SUPPORTED_CONTENT_TYPES = (
|
||||||
|
@ -51,22 +46,11 @@ _SUPPORTED_CONTENT_TYPES = (
|
||||||
'application/vnd.openstack.compute+json',
|
'application/vnd.openstack.compute+json',
|
||||||
)
|
)
|
||||||
|
|
||||||
_SUPPORTED_XML_CONTENT_TYPES = (
|
|
||||||
'application/xml',
|
|
||||||
'application/vnd.openstack.compute+xml',
|
|
||||||
)
|
|
||||||
|
|
||||||
_MEDIA_TYPE_MAP = {
|
_MEDIA_TYPE_MAP = {
|
||||||
'application/vnd.openstack.compute+json': 'json',
|
'application/vnd.openstack.compute+json': 'json',
|
||||||
'application/json': 'json',
|
'application/json': 'json',
|
||||||
}
|
}
|
||||||
|
|
||||||
_MEDIA_XML_TYPE_MAP = {
|
|
||||||
'application/vnd.openstack.compute+xml': 'xml',
|
|
||||||
'application/xml': 'xml',
|
|
||||||
'application/atom+xml': 'atom',
|
|
||||||
}
|
|
||||||
|
|
||||||
# These are typically automatically created by routes as either defaults
|
# These are typically automatically created by routes as either defaults
|
||||||
# collection or member methods.
|
# collection or member methods.
|
||||||
_ROUTES_METHODS = [
|
_ROUTES_METHODS = [
|
||||||
|
@ -94,21 +78,12 @@ VER_METHOD_ATTR = 'versioned_methods'
|
||||||
API_VERSION_REQUEST_HEADER = 'X-OpenStack-Compute-API-Version'
|
API_VERSION_REQUEST_HEADER = 'X-OpenStack-Compute-API-Version'
|
||||||
|
|
||||||
|
|
||||||
# TODO(dims): Temporary, we already deprecated the v2 XML API in
|
|
||||||
# Juno, we should remove this before Kilo
|
|
||||||
DISABLE_XML_V2_API = True
|
|
||||||
|
|
||||||
|
|
||||||
def get_supported_content_types():
|
def get_supported_content_types():
|
||||||
if DISABLE_XML_V2_API:
|
|
||||||
return _SUPPORTED_CONTENT_TYPES
|
return _SUPPORTED_CONTENT_TYPES
|
||||||
return _SUPPORTED_CONTENT_TYPES + _SUPPORTED_XML_CONTENT_TYPES
|
|
||||||
|
|
||||||
|
|
||||||
def get_media_map():
|
def get_media_map():
|
||||||
if DISABLE_XML_V2_API:
|
return dict(_MEDIA_TYPE_MAP.items())
|
||||||
return _MEDIA_TYPE_MAP
|
|
||||||
return dict(_MEDIA_TYPE_MAP.items() + _MEDIA_XML_TYPE_MAP.items())
|
|
||||||
|
|
||||||
|
|
||||||
class Request(webob.Request):
|
class Request(webob.Request):
|
||||||
|
@ -302,107 +277,6 @@ class JSONDeserializer(TextDeserializer):
|
||||||
return {'body': self._from_json(datastring)}
|
return {'body': self._from_json(datastring)}
|
||||||
|
|
||||||
|
|
||||||
class XMLDeserializer(TextDeserializer):
|
|
||||||
|
|
||||||
def __init__(self, metadata=None):
|
|
||||||
""":param metadata: information needed to deserialize xml into
|
|
||||||
a dictionary.
|
|
||||||
"""
|
|
||||||
super(XMLDeserializer, self).__init__()
|
|
||||||
self.metadata = metadata or {}
|
|
||||||
|
|
||||||
def _from_xml(self, datastring):
|
|
||||||
plurals = set(self.metadata.get('plurals', {}))
|
|
||||||
node = xmlutil.safe_minidom_parse_string(datastring).childNodes[0]
|
|
||||||
return {node.nodeName: self._from_xml_node(node, plurals)}
|
|
||||||
|
|
||||||
def _from_xml_node(self, node, listnames):
|
|
||||||
"""Convert a minidom node to a simple Python type.
|
|
||||||
|
|
||||||
:param listnames: list of XML node names whose subnodes should
|
|
||||||
be considered list items.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if len(node.childNodes) == 1 and node.childNodes[0].nodeType == 3:
|
|
||||||
return node.childNodes[0].nodeValue
|
|
||||||
elif node.nodeName in listnames:
|
|
||||||
return [self._from_xml_node(n, listnames) for n in node.childNodes]
|
|
||||||
else:
|
|
||||||
result = dict()
|
|
||||||
for attr in node.attributes.keys():
|
|
||||||
if not attr.startswith("xmlns"):
|
|
||||||
result[attr] = node.attributes[attr].nodeValue
|
|
||||||
for child in node.childNodes:
|
|
||||||
if child.nodeType != node.TEXT_NODE:
|
|
||||||
result[child.nodeName] = self._from_xml_node(child,
|
|
||||||
listnames)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def find_first_child_named_in_namespace(self, parent, namespace, name):
|
|
||||||
"""Search a nodes children for the first child with a given name."""
|
|
||||||
for node in parent.childNodes:
|
|
||||||
if (node.localName == name and
|
|
||||||
node.namespaceURI and
|
|
||||||
node.namespaceURI == namespace):
|
|
||||||
return node
|
|
||||||
return None
|
|
||||||
|
|
||||||
def find_first_child_named(self, parent, name):
|
|
||||||
"""Search a nodes children for the first child with a given name."""
|
|
||||||
for node in parent.childNodes:
|
|
||||||
if node.localName == name:
|
|
||||||
return node
|
|
||||||
return None
|
|
||||||
|
|
||||||
def find_children_named(self, parent, name):
|
|
||||||
"""Return all of a nodes children who have the given name."""
|
|
||||||
for node in parent.childNodes:
|
|
||||||
if node.localName == name:
|
|
||||||
yield node
|
|
||||||
|
|
||||||
def extract_text(self, node):
|
|
||||||
"""Get the text field contained by the given node."""
|
|
||||||
ret_val = ""
|
|
||||||
for child in node.childNodes:
|
|
||||||
if child.nodeType == child.TEXT_NODE:
|
|
||||||
ret_val += child.nodeValue
|
|
||||||
return ret_val
|
|
||||||
|
|
||||||
def extract_elements(self, node):
|
|
||||||
"""Get only Element type childs from node."""
|
|
||||||
elements = []
|
|
||||||
for child in node.childNodes:
|
|
||||||
if child.nodeType == child.ELEMENT_NODE:
|
|
||||||
elements.append(child)
|
|
||||||
return elements
|
|
||||||
|
|
||||||
def find_attribute_or_element(self, parent, name):
|
|
||||||
"""Get an attribute value; fallback to an element if not found."""
|
|
||||||
if parent.hasAttribute(name):
|
|
||||||
return parent.getAttribute(name)
|
|
||||||
|
|
||||||
node = self.find_first_child_named(parent, name)
|
|
||||||
if node:
|
|
||||||
return self.extract_text(node)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def default(self, datastring):
|
|
||||||
return {'body': self._from_xml(datastring)}
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataXMLDeserializer(XMLDeserializer):
|
|
||||||
|
|
||||||
def extract_metadata(self, metadata_node):
|
|
||||||
"""Marshal the metadata attribute of a parsed request."""
|
|
||||||
metadata = {}
|
|
||||||
if metadata_node is not None:
|
|
||||||
for meta_node in self.find_children_named(metadata_node, "meta"):
|
|
||||||
key = meta_node.getAttribute("key")
|
|
||||||
metadata[key] = self.extract_text(meta_node)
|
|
||||||
return metadata
|
|
||||||
|
|
||||||
|
|
||||||
class DictSerializer(ActionDispatcher):
|
class DictSerializer(ActionDispatcher):
|
||||||
"""Default request body serialization."""
|
"""Default request body serialization."""
|
||||||
|
|
||||||
|
@ -420,76 +294,6 @@ class JSONDictSerializer(DictSerializer):
|
||||||
return jsonutils.dumps(data)
|
return jsonutils.dumps(data)
|
||||||
|
|
||||||
|
|
||||||
class XMLDictSerializer(DictSerializer):
|
|
||||||
|
|
||||||
def __init__(self, metadata=None, xmlns=None):
|
|
||||||
""":param metadata: information needed to deserialize xml into
|
|
||||||
a dictionary.
|
|
||||||
:param xmlns: XML namespace to include with serialized xml
|
|
||||||
"""
|
|
||||||
super(XMLDictSerializer, self).__init__()
|
|
||||||
self.metadata = metadata or {}
|
|
||||||
self.xmlns = xmlns
|
|
||||||
|
|
||||||
def default(self, data):
|
|
||||||
# We expect data to contain a single key which is the XML root.
|
|
||||||
root_key = data.keys()[0]
|
|
||||||
doc = minidom.Document()
|
|
||||||
node = self._to_xml_node(doc, self.metadata, root_key, data[root_key])
|
|
||||||
|
|
||||||
return self.to_xml_string(node)
|
|
||||||
|
|
||||||
def to_xml_string(self, node, has_atom=False):
|
|
||||||
self._add_xmlns(node, has_atom)
|
|
||||||
return node.toxml('UTF-8')
|
|
||||||
|
|
||||||
# NOTE (ameade): the has_atom should be removed after all of the
|
|
||||||
# xml serializers and view builders have been updated to the current
|
|
||||||
# spec that required all responses include the xmlns:atom, the has_atom
|
|
||||||
# flag is to prevent current tests from breaking
|
|
||||||
def _add_xmlns(self, node, has_atom=False):
|
|
||||||
if self.xmlns is not None:
|
|
||||||
node.setAttribute('xmlns', self.xmlns)
|
|
||||||
if has_atom:
|
|
||||||
node.setAttribute('xmlns:atom', "http://www.w3.org/2005/Atom")
|
|
||||||
|
|
||||||
def _to_xml_node(self, doc, metadata, nodename, data):
|
|
||||||
"""Recursive method to convert data members to XML nodes."""
|
|
||||||
result = doc.createElement(nodename)
|
|
||||||
|
|
||||||
# TODO(bcwaldon): accomplish this without a type-check
|
|
||||||
if isinstance(data, list):
|
|
||||||
if nodename.endswith('s'):
|
|
||||||
singular = nodename[:-1]
|
|
||||||
else:
|
|
||||||
singular = 'item'
|
|
||||||
for item in data:
|
|
||||||
node = self._to_xml_node(doc, metadata, singular, item)
|
|
||||||
result.appendChild(node)
|
|
||||||
# TODO(bcwaldon): accomplish this without a type-check
|
|
||||||
elif isinstance(data, dict):
|
|
||||||
attrs = metadata.get('attributes', {}).get(nodename, {})
|
|
||||||
for k, v in data.items():
|
|
||||||
if k in attrs:
|
|
||||||
result.setAttribute(k, str(v))
|
|
||||||
else:
|
|
||||||
if k == "deleted":
|
|
||||||
v = str(bool(v))
|
|
||||||
node = self._to_xml_node(doc, metadata, k, v)
|
|
||||||
result.appendChild(node)
|
|
||||||
else:
|
|
||||||
# Type is atom
|
|
||||||
if not isinstance(data, six.string_types):
|
|
||||||
data = six.text_type(data)
|
|
||||||
node = doc.createTextNode(data)
|
|
||||||
result.appendChild(node)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _to_xml(self, root):
|
|
||||||
"""Convert the xml object to an xml string."""
|
|
||||||
return etree.tostring(root, encoding='UTF-8', xml_declaration=True)
|
|
||||||
|
|
||||||
|
|
||||||
def serializers(**serializers):
|
def serializers(**serializers):
|
||||||
"""Attaches serializers to a method.
|
"""Attaches serializers to a method.
|
||||||
|
|
||||||
|
@ -688,15 +492,6 @@ def action_peek_json(body):
|
||||||
return decoded.keys()[0]
|
return decoded.keys()[0]
|
||||||
|
|
||||||
|
|
||||||
def action_peek_xml(body):
|
|
||||||
"""Determine action to invoke."""
|
|
||||||
|
|
||||||
dom = xmlutil.safe_minidom_parse_string(body)
|
|
||||||
action_node = dom.childNodes[0]
|
|
||||||
|
|
||||||
return action_node.tagName
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceExceptionHandler(object):
|
class ResourceExceptionHandler(object):
|
||||||
"""Context manager to handle Resource exceptions.
|
"""Context manager to handle Resource exceptions.
|
||||||
|
|
||||||
|
@ -767,16 +562,13 @@ class Resource(wsgi.Application):
|
||||||
|
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
|
|
||||||
default_deserializers = dict(xml=XMLDeserializer,
|
default_deserializers = dict(json=JSONDeserializer)
|
||||||
json=JSONDeserializer)
|
|
||||||
default_deserializers.update(deserializers)
|
default_deserializers.update(deserializers)
|
||||||
|
|
||||||
self.default_deserializers = default_deserializers
|
self.default_deserializers = default_deserializers
|
||||||
self.default_serializers = dict(xml=XMLDictSerializer,
|
self.default_serializers = dict(json=JSONDictSerializer)
|
||||||
json=JSONDictSerializer)
|
|
||||||
|
|
||||||
self.action_peek = dict(xml=action_peek_xml,
|
self.action_peek = dict(json=action_peek_json)
|
||||||
json=action_peek_json)
|
|
||||||
self.action_peek.update(action_peek or {})
|
self.action_peek.update(action_peek or {})
|
||||||
|
|
||||||
# Copy over the actions dictionary
|
# Copy over the actions dictionary
|
||||||
|
@ -1385,14 +1177,8 @@ class Fault(webob.exc.HTTPException):
|
||||||
self.wrapped_exc.headers['Vary'] = \
|
self.wrapped_exc.headers['Vary'] = \
|
||||||
API_VERSION_REQUEST_HEADER
|
API_VERSION_REQUEST_HEADER
|
||||||
|
|
||||||
# 'code' is an attribute on the fault tag itself
|
|
||||||
metadata = {'attributes': {fault_name: 'code'}}
|
|
||||||
|
|
||||||
xml_serializer = XMLDictSerializer(metadata, XMLNS_V11)
|
|
||||||
|
|
||||||
content_type = req.best_match_content_type()
|
content_type = req.best_match_content_type()
|
||||||
serializer = {
|
serializer = {
|
||||||
'application/xml': xml_serializer,
|
|
||||||
'application/json': JSONDictSerializer(),
|
'application/json': JSONDictSerializer(),
|
||||||
}[content_type]
|
}[content_type]
|
||||||
|
|
||||||
|
@ -1435,16 +1221,13 @@ class RateLimitFault(webob.exc.HTTPException):
|
||||||
"""
|
"""
|
||||||
user_locale = request.best_match_language()
|
user_locale = request.best_match_language()
|
||||||
content_type = request.best_match_content_type()
|
content_type = request.best_match_content_type()
|
||||||
metadata = {"attributes": {"overLimit": ["code", "retryAfter"]}}
|
|
||||||
|
|
||||||
self.content['overLimit']['message'] = \
|
self.content['overLimit']['message'] = \
|
||||||
i18n.translate(self.content['overLimit']['message'], user_locale)
|
i18n.translate(self.content['overLimit']['message'], user_locale)
|
||||||
self.content['overLimit']['details'] = \
|
self.content['overLimit']['details'] = \
|
||||||
i18n.translate(self.content['overLimit']['details'], user_locale)
|
i18n.translate(self.content['overLimit']['details'], user_locale)
|
||||||
|
|
||||||
xml_serializer = XMLDictSerializer(metadata, XMLNS_V11)
|
|
||||||
serializer = {
|
serializer = {
|
||||||
'application/xml': xml_serializer,
|
|
||||||
'application/json': JSONDictSerializer(),
|
'application/json': JSONDictSerializer(),
|
||||||
}[content_type]
|
}[content_type]
|
||||||
|
|
||||||
|
|
|
@ -1,995 +0,0 @@
|
||||||
# Copyright 2011 OpenStack Foundation
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
from xml.dom import minidom
|
|
||||||
from xml.parsers import expat
|
|
||||||
from xml import sax
|
|
||||||
from xml.sax import expatreader
|
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
import six
|
|
||||||
|
|
||||||
from nova import exception
|
|
||||||
from nova.i18n import _
|
|
||||||
from nova import utils
|
|
||||||
|
|
||||||
XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
|
|
||||||
XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
|
|
||||||
XMLNS_COMMON_V10 = 'http://docs.openstack.org/common/api/v1.0'
|
|
||||||
XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
|
|
||||||
|
|
||||||
|
|
||||||
def validate_schema(xml, schema_name, version='v1.1'):
|
|
||||||
if isinstance(xml, str):
|
|
||||||
xml = etree.fromstring(xml)
|
|
||||||
base_path = 'nova/api/openstack/compute/schemas/'
|
|
||||||
if schema_name not in ('atom', 'atom-link'):
|
|
||||||
base_path += '%s/' % version
|
|
||||||
schema_path = os.path.join(utils.novadir(),
|
|
||||||
'%s%s.rng' % (base_path, schema_name))
|
|
||||||
schema_doc = etree.parse(schema_path)
|
|
||||||
relaxng = etree.RelaxNG(schema_doc)
|
|
||||||
relaxng.assertValid(xml)
|
|
||||||
|
|
||||||
|
|
||||||
class Selector(object):
|
|
||||||
"""Selects datum to operate on from an object."""
|
|
||||||
|
|
||||||
def __init__(self, *chain):
|
|
||||||
"""Initialize the selector.
|
|
||||||
|
|
||||||
Each argument is a subsequent index into the object.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.chain = chain
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Return a representation of the selector."""
|
|
||||||
|
|
||||||
return "Selector" + repr(self.chain)
|
|
||||||
|
|
||||||
def __call__(self, obj, do_raise=False):
|
|
||||||
"""Select a datum to operate on.
|
|
||||||
|
|
||||||
Selects the relevant datum within the object.
|
|
||||||
|
|
||||||
:param obj: The object from which to select the object.
|
|
||||||
:param do_raise: If False (the default), return None if the
|
|
||||||
indexed datum does not exist. Otherwise,
|
|
||||||
raise a KeyError.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Walk the selector list
|
|
||||||
for elem in self.chain:
|
|
||||||
# If it's callable, call it
|
|
||||||
if callable(elem):
|
|
||||||
obj = elem(obj)
|
|
||||||
else:
|
|
||||||
if obj == '':
|
|
||||||
return ''
|
|
||||||
# Use indexing
|
|
||||||
try:
|
|
||||||
obj = obj[elem]
|
|
||||||
except (KeyError, IndexError):
|
|
||||||
# No sense going any further
|
|
||||||
if do_raise:
|
|
||||||
# Convert to a KeyError, for consistency
|
|
||||||
raise KeyError(elem)
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Return the finally-selected object
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def get_items(obj):
|
|
||||||
"""Get items in obj."""
|
|
||||||
|
|
||||||
return list(obj.items())
|
|
||||||
|
|
||||||
|
|
||||||
def get_items_without_dict(obj):
|
|
||||||
"""Get items in obj but omit any items containing a dict."""
|
|
||||||
|
|
||||||
obj_list = list(obj.items())
|
|
||||||
for item in obj_list:
|
|
||||||
if isinstance(list(item)[1], dict):
|
|
||||||
obj_list.remove(item)
|
|
||||||
return obj_list
|
|
||||||
|
|
||||||
|
|
||||||
class EmptyStringSelector(Selector):
|
|
||||||
"""Returns the empty string if Selector would return None."""
|
|
||||||
def __call__(self, obj, do_raise=False):
|
|
||||||
"""Returns empty string if the selected value does not exist."""
|
|
||||||
|
|
||||||
try:
|
|
||||||
return super(EmptyStringSelector, self).__call__(obj, True)
|
|
||||||
except KeyError:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
class ConstantSelector(object):
|
|
||||||
"""Returns a constant."""
|
|
||||||
|
|
||||||
def __init__(self, value):
|
|
||||||
"""Initialize the selector.
|
|
||||||
|
|
||||||
:param value: The value to return.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.value = value
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Return a representation of the selector."""
|
|
||||||
|
|
||||||
return repr(self.value)
|
|
||||||
|
|
||||||
def __call__(self, _obj, _do_raise=False):
|
|
||||||
"""Select a datum to operate on.
|
|
||||||
|
|
||||||
Returns a constant value. Compatible with
|
|
||||||
Selector.__call__().
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self.value
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateElement(object):
|
|
||||||
"""Represent an element in the template."""
|
|
||||||
|
|
||||||
def __init__(self, tag, attrib=None, selector=None, subselector=None,
|
|
||||||
colon_ns=False, **extra):
|
|
||||||
"""Initialize an element.
|
|
||||||
|
|
||||||
Initializes an element in the template. Keyword arguments
|
|
||||||
specify attributes to be set on the element; values must be
|
|
||||||
callables. See TemplateElement.set() for more information.
|
|
||||||
|
|
||||||
:param tag: The name of the tag to create.
|
|
||||||
:param attrib: An optional dictionary of element attributes.
|
|
||||||
:param selector: An optional callable taking an object and
|
|
||||||
optional boolean do_raise indicator and
|
|
||||||
returning the object bound to the element.
|
|
||||||
:param subselector: An optional callable taking an object and
|
|
||||||
optional boolean do_raise indicator and
|
|
||||||
returning the object bound to the element.
|
|
||||||
This is used to further refine the datum
|
|
||||||
object returned by selector in the event
|
|
||||||
that it is a list of objects.
|
|
||||||
:colon_ns: An optional flag indicating whether to support k:v
|
|
||||||
type tagname, if True the k:v type tagname will
|
|
||||||
be supported by adding the k into the namespace.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Convert selector into a Selector
|
|
||||||
if selector is None:
|
|
||||||
selector = Selector()
|
|
||||||
elif not callable(selector):
|
|
||||||
selector = Selector(selector)
|
|
||||||
|
|
||||||
# Convert subselector into a Selector
|
|
||||||
if subselector is not None and not callable(subselector):
|
|
||||||
subselector = Selector(subselector)
|
|
||||||
|
|
||||||
self.tag = tag
|
|
||||||
self.selector = selector
|
|
||||||
self.subselector = subselector
|
|
||||||
self.attrib = {}
|
|
||||||
self._text = None
|
|
||||||
self._children = []
|
|
||||||
self._childmap = {}
|
|
||||||
self.colon_ns = colon_ns
|
|
||||||
|
|
||||||
# Run the incoming attributes through set() so that they
|
|
||||||
# become selectorized
|
|
||||||
if not attrib:
|
|
||||||
attrib = {}
|
|
||||||
attrib.update(extra)
|
|
||||||
for k, v in attrib.items():
|
|
||||||
self.set(k, v)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Return a representation of the template element."""
|
|
||||||
|
|
||||||
return ('<%s.%s %r at %#x>' %
|
|
||||||
(self.__class__.__module__, self.__class__.__name__,
|
|
||||||
self.tag, id(self)))
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
"""Return the number of child elements."""
|
|
||||||
|
|
||||||
return len(self._children)
|
|
||||||
|
|
||||||
def __contains__(self, key):
|
|
||||||
"""Determine whether a child node named by key exists."""
|
|
||||||
|
|
||||||
return key in self._childmap
|
|
||||||
|
|
||||||
def __getitem__(self, idx):
|
|
||||||
"""Retrieve a child node by index or name."""
|
|
||||||
|
|
||||||
if isinstance(idx, six.string_types):
|
|
||||||
# Allow access by node name
|
|
||||||
return self._childmap[idx]
|
|
||||||
else:
|
|
||||||
return self._children[idx]
|
|
||||||
|
|
||||||
def append(self, elem):
|
|
||||||
"""Append a child to the element."""
|
|
||||||
|
|
||||||
# Unwrap templates...
|
|
||||||
elem = elem.unwrap()
|
|
||||||
|
|
||||||
# Avoid duplications
|
|
||||||
if elem.tag in self._childmap:
|
|
||||||
raise KeyError(elem.tag)
|
|
||||||
|
|
||||||
self._children.append(elem)
|
|
||||||
self._childmap[elem.tag] = elem
|
|
||||||
|
|
||||||
def extend(self, elems):
|
|
||||||
"""Append children to the element."""
|
|
||||||
|
|
||||||
# Pre-evaluate the elements
|
|
||||||
elemmap = {}
|
|
||||||
elemlist = []
|
|
||||||
for elem in elems:
|
|
||||||
# Unwrap templates...
|
|
||||||
elem = elem.unwrap()
|
|
||||||
|
|
||||||
# Avoid duplications
|
|
||||||
if elem.tag in self._childmap or elem.tag in elemmap:
|
|
||||||
raise KeyError(elem.tag)
|
|
||||||
|
|
||||||
elemmap[elem.tag] = elem
|
|
||||||
elemlist.append(elem)
|
|
||||||
|
|
||||||
# Update the children
|
|
||||||
self._children.extend(elemlist)
|
|
||||||
self._childmap.update(elemmap)
|
|
||||||
|
|
||||||
def insert(self, idx, elem):
|
|
||||||
"""Insert a child element at the given index."""
|
|
||||||
|
|
||||||
# Unwrap templates...
|
|
||||||
elem = elem.unwrap()
|
|
||||||
|
|
||||||
# Avoid duplications
|
|
||||||
if elem.tag in self._childmap:
|
|
||||||
raise KeyError(elem.tag)
|
|
||||||
|
|
||||||
self._children.insert(idx, elem)
|
|
||||||
self._childmap[elem.tag] = elem
|
|
||||||
|
|
||||||
def remove(self, elem):
|
|
||||||
"""Remove a child element."""
|
|
||||||
|
|
||||||
# Unwrap templates...
|
|
||||||
elem = elem.unwrap()
|
|
||||||
|
|
||||||
# Check if element exists
|
|
||||||
if elem.tag not in self._childmap or self._childmap[elem.tag] != elem:
|
|
||||||
raise ValueError(_('element is not a child'))
|
|
||||||
|
|
||||||
self._children.remove(elem)
|
|
||||||
del self._childmap[elem.tag]
|
|
||||||
|
|
||||||
def get(self, key):
|
|
||||||
"""Get an attribute.
|
|
||||||
|
|
||||||
Returns a callable which performs datum selection.
|
|
||||||
|
|
||||||
:param key: The name of the attribute to get.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self.attrib[key]
|
|
||||||
|
|
||||||
def set(self, key, value=None):
|
|
||||||
"""Set an attribute.
|
|
||||||
|
|
||||||
:param key: The name of the attribute to set.
|
|
||||||
|
|
||||||
:param value: A callable taking an object and optional boolean
|
|
||||||
do_raise indicator and returning the datum bound
|
|
||||||
to the attribute. If None, a Selector() will be
|
|
||||||
constructed from the key. If a string, a
|
|
||||||
Selector() will be constructed from the string.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Convert value to a selector
|
|
||||||
if value is None:
|
|
||||||
value = Selector(key)
|
|
||||||
elif not callable(value):
|
|
||||||
value = Selector(value)
|
|
||||||
|
|
||||||
self.attrib[key] = value
|
|
||||||
|
|
||||||
def keys(self):
|
|
||||||
"""Return the attribute names."""
|
|
||||||
|
|
||||||
return self.attrib.keys()
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
"""Return the attribute names and values."""
|
|
||||||
|
|
||||||
return self.attrib.items()
|
|
||||||
|
|
||||||
def unwrap(self):
|
|
||||||
"""Unwraps a template to return a template element."""
|
|
||||||
|
|
||||||
# We are a template element
|
|
||||||
return self
|
|
||||||
|
|
||||||
def wrap(self):
|
|
||||||
"""Wraps a template element to return a template."""
|
|
||||||
|
|
||||||
# Wrap in a basic Template
|
|
||||||
return Template(self)
|
|
||||||
|
|
||||||
def apply(self, elem, obj):
|
|
||||||
"""Apply text and attributes to an etree.Element.
|
|
||||||
|
|
||||||
Applies the text and attribute instructions in the template
|
|
||||||
element to an etree.Element instance.
|
|
||||||
|
|
||||||
:param elem: An etree.Element instance.
|
|
||||||
:param obj: The base object associated with this template
|
|
||||||
element.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Start with the text...
|
|
||||||
if self.text is not None:
|
|
||||||
elem.text = unicode(self.text(obj))
|
|
||||||
|
|
||||||
# Now set up all the attributes...
|
|
||||||
for key, value in self.attrib.items():
|
|
||||||
try:
|
|
||||||
elem.set(key, unicode(value(obj, True)))
|
|
||||||
except KeyError:
|
|
||||||
# Attribute has no value, so don't include it
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _render(self, parent, datum, patches, nsmap):
|
|
||||||
"""Internal rendering.
|
|
||||||
|
|
||||||
Renders the template node into an etree.Element object.
|
|
||||||
Returns the etree.Element object.
|
|
||||||
|
|
||||||
:param parent: The parent etree.Element instance.
|
|
||||||
:param datum: The datum associated with this template element.
|
|
||||||
:param patches: A list of other template elements that must
|
|
||||||
also be applied.
|
|
||||||
:param nsmap: An optional namespace dictionary to be
|
|
||||||
associated with the etree.Element instance.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Allocate a node
|
|
||||||
if callable(self.tag):
|
|
||||||
tagname = self.tag(datum)
|
|
||||||
else:
|
|
||||||
tagname = self.tag
|
|
||||||
|
|
||||||
if self.colon_ns:
|
|
||||||
if ':' in tagname:
|
|
||||||
if nsmap is None:
|
|
||||||
nsmap = {}
|
|
||||||
colon_key, colon_name = tagname.split(':')
|
|
||||||
nsmap[colon_key] = colon_key
|
|
||||||
tagname = '{%s}%s' % (colon_key, colon_name)
|
|
||||||
|
|
||||||
elem = etree.Element(tagname, nsmap=nsmap)
|
|
||||||
|
|
||||||
# If we have a parent, append the node to the parent
|
|
||||||
if parent is not None:
|
|
||||||
parent.append(elem)
|
|
||||||
|
|
||||||
# If the datum is None, do nothing else
|
|
||||||
if datum is None:
|
|
||||||
return elem
|
|
||||||
|
|
||||||
# Apply this template element to the element
|
|
||||||
self.apply(elem, datum)
|
|
||||||
|
|
||||||
# Additionally, apply the patches
|
|
||||||
for patch in patches:
|
|
||||||
patch.apply(elem, datum)
|
|
||||||
|
|
||||||
# We have fully rendered the element; return it
|
|
||||||
return elem
|
|
||||||
|
|
||||||
def render(self, parent, obj, patches=None, nsmap=None):
|
|
||||||
"""Render an object.
|
|
||||||
|
|
||||||
Renders an object against this template node. Returns a list
|
|
||||||
of two-item tuples, where the first item is an etree.Element
|
|
||||||
instance and the second item is the datum associated with that
|
|
||||||
instance.
|
|
||||||
|
|
||||||
:param parent: The parent for the etree.Element instances.
|
|
||||||
:param obj: The object to render this template element
|
|
||||||
against.
|
|
||||||
:param patches: A list of other template elements to apply
|
|
||||||
when rendering this template element.
|
|
||||||
:param nsmap: An optional namespace dictionary to attach to
|
|
||||||
the etree.Element instances.
|
|
||||||
"""
|
|
||||||
|
|
||||||
patches = patches or []
|
|
||||||
# First, get the datum we're rendering
|
|
||||||
data = None if obj is None else self.selector(obj)
|
|
||||||
|
|
||||||
# Check if we should render at all
|
|
||||||
if not self.will_render(data):
|
|
||||||
return []
|
|
||||||
elif data is None:
|
|
||||||
return [(self._render(parent, None, patches, nsmap), None)]
|
|
||||||
|
|
||||||
# Make the data into a list if it isn't already
|
|
||||||
if not isinstance(data, list):
|
|
||||||
data = [data]
|
|
||||||
elif parent is None:
|
|
||||||
raise ValueError(_('root element selecting a list'))
|
|
||||||
|
|
||||||
# Render all the elements
|
|
||||||
elems = []
|
|
||||||
for datum in data:
|
|
||||||
if self.subselector is not None:
|
|
||||||
datum = self.subselector(datum)
|
|
||||||
elems.append((self._render(parent, datum, patches, nsmap), datum))
|
|
||||||
|
|
||||||
# Return all the elements rendered, as well as the
|
|
||||||
# corresponding datum for the next step down the tree
|
|
||||||
return elems
|
|
||||||
|
|
||||||
def will_render(self, datum):
|
|
||||||
"""Hook method.
|
|
||||||
|
|
||||||
An overridable hook method to determine whether this template
|
|
||||||
element will be rendered at all. By default, returns False
|
|
||||||
(inhibiting rendering) if the datum is None.
|
|
||||||
|
|
||||||
:param datum: The datum associated with this template element.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Don't render if datum is None
|
|
||||||
return datum is not None
|
|
||||||
|
|
||||||
def _text_get(self):
|
|
||||||
"""Template element text.
|
|
||||||
|
|
||||||
Either None or a callable taking an object and optional
|
|
||||||
boolean do_raise indicator and returning the datum bound to
|
|
||||||
the text of the template element.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self._text
|
|
||||||
|
|
||||||
def _text_set(self, value):
|
|
||||||
# Convert value to a selector
|
|
||||||
if value is not None and not callable(value):
|
|
||||||
value = Selector(value)
|
|
||||||
|
|
||||||
self._text = value
|
|
||||||
|
|
||||||
def _text_del(self):
|
|
||||||
self._text = None
|
|
||||||
|
|
||||||
text = property(_text_get, _text_set, _text_del)
|
|
||||||
|
|
||||||
def tree(self):
|
|
||||||
"""Return string representation of the template tree.
|
|
||||||
|
|
||||||
Returns a representation of the template rooted at this
|
|
||||||
element as a string, suitable for inclusion in debug logs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Build the inner contents of the tag...
|
|
||||||
contents = [self.tag, '!selector=%r' % self.selector]
|
|
||||||
|
|
||||||
# Add the text...
|
|
||||||
if self.text is not None:
|
|
||||||
contents.append('!text=%r' % self.text)
|
|
||||||
|
|
||||||
# Add all the other attributes
|
|
||||||
for key, value in self.attrib.items():
|
|
||||||
contents.append('%s=%r' % (key, value))
|
|
||||||
|
|
||||||
# If there are no children, return it as a closed tag
|
|
||||||
if len(self) == 0:
|
|
||||||
return '<%s/>' % ' '.join([str(i) for i in contents])
|
|
||||||
|
|
||||||
# OK, recurse to our children
|
|
||||||
children = [c.tree() for c in self]
|
|
||||||
|
|
||||||
# Return the result
|
|
||||||
return ('<%s>%s</%s>' %
|
|
||||||
(' '.join(contents), ''.join(children), self.tag))
|
|
||||||
|
|
||||||
|
|
||||||
def SubTemplateElement(parent, tag, attrib=None, selector=None,
|
|
||||||
subselector=None, colon_ns=False, **extra):
|
|
||||||
"""Create a template element as a child of another.
|
|
||||||
|
|
||||||
Corresponds to the etree.SubElement interface. Parameters are as
|
|
||||||
for TemplateElement, with the addition of the parent.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Convert attributes
|
|
||||||
attrib = attrib or {}
|
|
||||||
attrib.update(extra)
|
|
||||||
|
|
||||||
# Get a TemplateElement
|
|
||||||
elem = TemplateElement(tag, attrib=attrib, selector=selector,
|
|
||||||
subselector=subselector, colon_ns=colon_ns)
|
|
||||||
|
|
||||||
# Append the parent safely
|
|
||||||
if parent is not None:
|
|
||||||
parent.append(elem)
|
|
||||||
|
|
||||||
return elem
|
|
||||||
|
|
||||||
|
|
||||||
class Template(object):
|
|
||||||
"""Represent a template."""
|
|
||||||
|
|
||||||
def __init__(self, root, nsmap=None):
|
|
||||||
"""Initialize a template.
|
|
||||||
|
|
||||||
:param root: The root element of the template.
|
|
||||||
:param nsmap: An optional namespace dictionary to be
|
|
||||||
associated with the root element of the
|
|
||||||
template.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.root = root.unwrap() if root is not None else None
|
|
||||||
self.nsmap = nsmap or {}
|
|
||||||
self.serialize_options = dict(encoding='UTF-8', xml_declaration=True)
|
|
||||||
|
|
||||||
def _serialize(self, parent, obj, siblings, nsmap=None):
|
|
||||||
"""Internal serialization.
|
|
||||||
|
|
||||||
Recursive routine to build a tree of etree.Element instances
|
|
||||||
from an object based on the template. Returns the first
|
|
||||||
etree.Element instance rendered, or None.
|
|
||||||
|
|
||||||
:param parent: The parent etree.Element instance. Can be
|
|
||||||
None.
|
|
||||||
:param obj: The object to render.
|
|
||||||
:param siblings: The TemplateElement instances against which
|
|
||||||
to render the object.
|
|
||||||
:param nsmap: An optional namespace dictionary to be
|
|
||||||
associated with the etree.Element instance
|
|
||||||
rendered.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# First step, render the element
|
|
||||||
elems = siblings[0].render(parent, obj, siblings[1:], nsmap)
|
|
||||||
|
|
||||||
# Now, recurse to all child elements
|
|
||||||
seen = set()
|
|
||||||
for idx, sibling in enumerate(siblings):
|
|
||||||
for child in sibling:
|
|
||||||
# Have we handled this child already?
|
|
||||||
if child.tag in seen:
|
|
||||||
continue
|
|
||||||
seen.add(child.tag)
|
|
||||||
|
|
||||||
# Determine the child's siblings
|
|
||||||
nieces = [child]
|
|
||||||
for sib in siblings[idx + 1:]:
|
|
||||||
if child.tag in sib:
|
|
||||||
nieces.append(sib[child.tag])
|
|
||||||
|
|
||||||
# Now we recurse for every data element
|
|
||||||
for elem, datum in elems:
|
|
||||||
self._serialize(elem, datum, nieces)
|
|
||||||
|
|
||||||
# Return the first element; at the top level, this will be the
|
|
||||||
# root element
|
|
||||||
if elems:
|
|
||||||
return elems[0][0]
|
|
||||||
|
|
||||||
def serialize(self, obj, *args, **kwargs):
|
|
||||||
"""Serialize an object.
|
|
||||||
|
|
||||||
Serializes an object against the template. Returns a string
|
|
||||||
with the serialized XML. Positional and keyword arguments are
|
|
||||||
passed to etree.tostring().
|
|
||||||
|
|
||||||
:param obj: The object to serialize.
|
|
||||||
"""
|
|
||||||
|
|
||||||
elem = self.make_tree(obj)
|
|
||||||
if elem is None:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
for k, v in self.serialize_options.items():
|
|
||||||
kwargs.setdefault(k, v)
|
|
||||||
|
|
||||||
# Serialize it into XML
|
|
||||||
return etree.tostring(elem, *args, **kwargs)
|
|
||||||
|
|
||||||
def make_tree(self, obj):
|
|
||||||
"""Create a tree.
|
|
||||||
|
|
||||||
Serializes an object against the template. Returns an Element
|
|
||||||
node with appropriate children.
|
|
||||||
|
|
||||||
:param obj: The object to serialize.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# If the template is empty, return the empty string
|
|
||||||
if self.root is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Get the siblings and nsmap of the root element
|
|
||||||
siblings = self._siblings()
|
|
||||||
nsmap = self._nsmap()
|
|
||||||
|
|
||||||
# Form the element tree
|
|
||||||
return self._serialize(None, obj, siblings, nsmap)
|
|
||||||
|
|
||||||
def _siblings(self):
|
|
||||||
"""Hook method for computing root siblings.
|
|
||||||
|
|
||||||
An overridable hook method to return the siblings of the root
|
|
||||||
element. By default, this is the root element itself.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return [self.root]
|
|
||||||
|
|
||||||
def _nsmap(self):
|
|
||||||
"""Hook method for computing the namespace dictionary.
|
|
||||||
|
|
||||||
An overridable hook method to return the namespace dictionary.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self.nsmap.copy()
|
|
||||||
|
|
||||||
def unwrap(self):
|
|
||||||
"""Unwraps a template to return a template element."""
|
|
||||||
|
|
||||||
# Return the root element
|
|
||||||
return self.root
|
|
||||||
|
|
||||||
def wrap(self):
|
|
||||||
"""Wraps a template element to return a template."""
|
|
||||||
|
|
||||||
# We are a template
|
|
||||||
return self
|
|
||||||
|
|
||||||
def apply(self, master):
|
|
||||||
"""Hook method for determining slave applicability.
|
|
||||||
|
|
||||||
An overridable hook method used to determine if this template
|
|
||||||
is applicable as a slave to a given master template.
|
|
||||||
|
|
||||||
:param master: The master template to test.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def tree(self):
|
|
||||||
"""Return string representation of the template tree.
|
|
||||||
|
|
||||||
Returns a representation of the template as a string, suitable
|
|
||||||
for inclusion in debug logs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return "%r: %s" % (self, self.root.tree())
|
|
||||||
|
|
||||||
|
|
||||||
class MasterTemplate(Template):
|
|
||||||
"""Represent a master template.
|
|
||||||
|
|
||||||
Master templates are versioned derivatives of templates that
|
|
||||||
additionally allow slave templates to be attached. Slave
|
|
||||||
templates allow modification of the serialized result without
|
|
||||||
directly changing the master.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, root, version, nsmap=None):
|
|
||||||
"""Initialize a master template.
|
|
||||||
|
|
||||||
:param root: The root element of the template.
|
|
||||||
:param version: The version number of the template.
|
|
||||||
:param nsmap: An optional namespace dictionary to be
|
|
||||||
associated with the root element of the
|
|
||||||
template.
|
|
||||||
"""
|
|
||||||
|
|
||||||
super(MasterTemplate, self).__init__(root, nsmap)
|
|
||||||
self.version = version
|
|
||||||
self.slaves = []
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Return string representation of the template."""
|
|
||||||
|
|
||||||
return ("<%s.%s object version %s at %#x>" %
|
|
||||||
(self.__class__.__module__, self.__class__.__name__,
|
|
||||||
self.version, id(self)))
|
|
||||||
|
|
||||||
def _siblings(self):
|
|
||||||
"""Hook method for computing root siblings.
|
|
||||||
|
|
||||||
An overridable hook method to return the siblings of the root
|
|
||||||
element. This is the root element plus the root elements of
|
|
||||||
all the slave templates.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return [self.root] + [slave.root for slave in self.slaves]
|
|
||||||
|
|
||||||
def _nsmap(self):
|
|
||||||
"""Hook method for computing the namespace dictionary.
|
|
||||||
|
|
||||||
An overridable hook method to return the namespace dictionary.
|
|
||||||
The namespace dictionary is computed by taking the master
|
|
||||||
template's namespace dictionary and updating it from all the
|
|
||||||
slave templates.
|
|
||||||
"""
|
|
||||||
|
|
||||||
nsmap = self.nsmap.copy()
|
|
||||||
for slave in self.slaves:
|
|
||||||
nsmap.update(slave._nsmap())
|
|
||||||
return nsmap
|
|
||||||
|
|
||||||
def attach(self, *slaves):
|
|
||||||
"""Attach one or more slave templates.
|
|
||||||
|
|
||||||
Attaches one or more slave templates to the master template.
|
|
||||||
Slave templates must have a root element with the same tag as
|
|
||||||
the master template. The slave template's apply() method will
|
|
||||||
be called to determine if the slave should be applied to this
|
|
||||||
master; if it returns False, that slave will be skipped.
|
|
||||||
(This allows filtering of slaves based on the version of the
|
|
||||||
master template.)
|
|
||||||
"""
|
|
||||||
|
|
||||||
slave_list = []
|
|
||||||
for slave in slaves:
|
|
||||||
slave = slave.wrap()
|
|
||||||
|
|
||||||
# Make sure we have a tree match
|
|
||||||
if slave.root.tag != self.root.tag:
|
|
||||||
msg = _("Template tree mismatch; adding slave %(slavetag)s to "
|
|
||||||
"master %(mastertag)s") % {'slavetag': slave.root.tag,
|
|
||||||
'mastertag': self.root.tag}
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
# Make sure slave applies to this template
|
|
||||||
if not slave.apply(self):
|
|
||||||
continue
|
|
||||||
|
|
||||||
slave_list.append(slave)
|
|
||||||
|
|
||||||
# Add the slaves
|
|
||||||
self.slaves.extend(slave_list)
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
"""Return a copy of this master template."""
|
|
||||||
|
|
||||||
# Return a copy of the MasterTemplate
|
|
||||||
tmp = self.__class__(self.root, self.version, self.nsmap)
|
|
||||||
tmp.slaves = self.slaves[:]
|
|
||||||
return tmp
|
|
||||||
|
|
||||||
|
|
||||||
class SlaveTemplate(Template):
|
|
||||||
"""Represent a slave template.
|
|
||||||
|
|
||||||
Slave templates are versioned derivatives of templates. Each
|
|
||||||
slave has a minimum version and optional maximum version of the
|
|
||||||
master template to which they can be attached.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, root, min_vers, max_vers=None, nsmap=None):
|
|
||||||
"""Initialize a slave template.
|
|
||||||
|
|
||||||
:param root: The root element of the template.
|
|
||||||
:param min_vers: The minimum permissible version of the master
|
|
||||||
template for this slave template to apply.
|
|
||||||
:param max_vers: An optional upper bound for the master
|
|
||||||
template version.
|
|
||||||
:param nsmap: An optional namespace dictionary to be
|
|
||||||
associated with the root element of the
|
|
||||||
template.
|
|
||||||
"""
|
|
||||||
|
|
||||||
super(SlaveTemplate, self).__init__(root, nsmap)
|
|
||||||
self.min_vers = min_vers
|
|
||||||
self.max_vers = max_vers
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Return string representation of the template."""
|
|
||||||
|
|
||||||
return ("<%s.%s object versions %s-%s at %#x>" %
|
|
||||||
(self.__class__.__module__, self.__class__.__name__,
|
|
||||||
self.min_vers, self.max_vers, id(self)))
|
|
||||||
|
|
||||||
def apply(self, master):
|
|
||||||
"""Hook method for determining slave applicability.
|
|
||||||
|
|
||||||
An overridable hook method used to determine if this template
|
|
||||||
is applicable as a slave to a given master template. This
|
|
||||||
version requires the master template to have a version number
|
|
||||||
between min_vers and max_vers.
|
|
||||||
|
|
||||||
:param master: The master template to test.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Does the master meet our minimum version requirement?
|
|
||||||
if master.version < self.min_vers:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# How about our maximum version requirement?
|
|
||||||
if self.max_vers is not None and master.version > self.max_vers:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateBuilder(object):
|
|
||||||
"""Template builder.
|
|
||||||
|
|
||||||
This class exists to allow templates to be lazily built without
|
|
||||||
having to build them each time they are needed. It must be
|
|
||||||
subclassed, and the subclass must implement the construct()
|
|
||||||
method, which must return a Template (or subclass) instance. The
|
|
||||||
constructor will always return the template returned by
|
|
||||||
construct(), or, if it has a copy() method, a copy of that
|
|
||||||
template.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_tmpl = None
|
|
||||||
|
|
||||||
def __new__(cls, copy=True):
|
|
||||||
"""Construct and return a template.
|
|
||||||
|
|
||||||
:param copy: If True (the default), a copy of the template
|
|
||||||
will be constructed and returned, if possible.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Do we need to construct the template?
|
|
||||||
if cls._tmpl is None:
|
|
||||||
tmp = super(TemplateBuilder, cls).__new__(cls)
|
|
||||||
|
|
||||||
# Construct the template
|
|
||||||
cls._tmpl = tmp.construct()
|
|
||||||
|
|
||||||
# If the template has a copy attribute, return the result of
|
|
||||||
# calling it
|
|
||||||
if copy and hasattr(cls._tmpl, 'copy'):
|
|
||||||
return cls._tmpl.copy()
|
|
||||||
|
|
||||||
# Return the template
|
|
||||||
return cls._tmpl
|
|
||||||
|
|
||||||
def construct(self):
|
|
||||||
"""Construct a template.
|
|
||||||
|
|
||||||
Called to construct a template instance, which it must return.
|
|
||||||
Only called once.
|
|
||||||
"""
|
|
||||||
|
|
||||||
raise NotImplementedError(_("subclasses must implement construct()!"))
|
|
||||||
|
|
||||||
|
|
||||||
def make_links(parent, selector=None):
|
|
||||||
"""Attach an Atom <links> element to the parent."""
|
|
||||||
|
|
||||||
elem = SubTemplateElement(parent, '{%s}link' % XMLNS_ATOM,
|
|
||||||
selector=selector)
|
|
||||||
elem.set('rel')
|
|
||||||
elem.set('type')
|
|
||||||
elem.set('href')
|
|
||||||
|
|
||||||
# Just for completeness...
|
|
||||||
return elem
|
|
||||||
|
|
||||||
|
|
||||||
def make_flat_dict(name, selector=None, subselector=None,
|
|
||||||
ns=None, colon_ns=False, root=None,
|
|
||||||
ignore_sub_dicts=False):
|
|
||||||
"""Utility for simple XML templates that traditionally used
|
|
||||||
XMLDictSerializer with no metadata. Returns a template element
|
|
||||||
where the top-level element has the given tag name, and where
|
|
||||||
sub-elements have tag names derived from the object's keys and
|
|
||||||
text derived from the object's values.
|
|
||||||
|
|
||||||
:param root: if None, this will create the root.
|
|
||||||
:param ignore_sub_dicts: If True, ignores any dict objects inside the
|
|
||||||
object. If False, causes an error if there is a
|
|
||||||
dict object present.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Set up the names we need...
|
|
||||||
if ns is None:
|
|
||||||
elemname = name
|
|
||||||
tagname = Selector(0)
|
|
||||||
else:
|
|
||||||
elemname = '{%s}%s' % (ns, name)
|
|
||||||
tagname = lambda obj, do_raise=False: '{%s}%s' % (ns, obj[0])
|
|
||||||
|
|
||||||
if selector is None:
|
|
||||||
selector = name
|
|
||||||
if not root:
|
|
||||||
# Build the root element
|
|
||||||
root = TemplateElement(elemname, selector=selector,
|
|
||||||
subselector=subselector, colon_ns=colon_ns)
|
|
||||||
choice = get_items if ignore_sub_dicts is False else get_items_without_dict
|
|
||||||
# Build an element to represent all the keys and values
|
|
||||||
elem = SubTemplateElement(root, tagname, selector=choice,
|
|
||||||
colon_ns=colon_ns)
|
|
||||||
elem.text = 1
|
|
||||||
|
|
||||||
# Return the template
|
|
||||||
return root
|
|
||||||
|
|
||||||
|
|
||||||
class ProtectedExpatParser(expatreader.ExpatParser):
|
|
||||||
"""An expat parser which disables DTD's and entities by default."""
|
|
||||||
|
|
||||||
def __init__(self, forbid_dtd=True, forbid_entities=True,
|
|
||||||
*args, **kwargs):
|
|
||||||
# Python 2.x old style class
|
|
||||||
expatreader.ExpatParser.__init__(self, *args, **kwargs)
|
|
||||||
self.forbid_dtd = forbid_dtd
|
|
||||||
self.forbid_entities = forbid_entities
|
|
||||||
|
|
||||||
def start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
|
|
||||||
raise ValueError("Inline DTD forbidden")
|
|
||||||
|
|
||||||
def entity_decl(self, entityName, is_parameter_entity, value, base,
|
|
||||||
systemId, publicId, notationName):
|
|
||||||
raise ValueError("<!ENTITY> entity declaration forbidden")
|
|
||||||
|
|
||||||
def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
|
|
||||||
# expat 1.2
|
|
||||||
raise ValueError("<!ENTITY> unparsed entity forbidden")
|
|
||||||
|
|
||||||
def external_entity_ref(self, context, base, systemId, publicId):
|
|
||||||
raise ValueError("<!ENTITY> external entity forbidden")
|
|
||||||
|
|
||||||
def notation_decl(self, name, base, sysid, pubid):
|
|
||||||
raise ValueError("<!ENTITY> notation forbidden")
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
expatreader.ExpatParser.reset(self)
|
|
||||||
if self.forbid_dtd:
|
|
||||||
self._parser.StartDoctypeDeclHandler = self.start_doctype_decl
|
|
||||||
self._parser.EndDoctypeDeclHandler = None
|
|
||||||
if self.forbid_entities:
|
|
||||||
self._parser.EntityDeclHandler = self.entity_decl
|
|
||||||
self._parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl
|
|
||||||
self._parser.ExternalEntityRefHandler = self.external_entity_ref
|
|
||||||
self._parser.NotationDeclHandler = self.notation_decl
|
|
||||||
try:
|
|
||||||
self._parser.SkippedEntityHandler = None
|
|
||||||
except AttributeError:
|
|
||||||
# some pyexpat versions do not support SkippedEntity
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def safe_minidom_parse_string(xml_string):
|
|
||||||
"""Parse an XML string using minidom safely."""
|
|
||||||
try:
|
|
||||||
return minidom.parseString(xml_string, parser=ProtectedExpatParser())
|
|
||||||
except (sax.SAXParseException, ValueError,
|
|
||||||
expat.ExpatError, LookupError) as e:
|
|
||||||
# NOTE(Vijaya Erukala): XML input such as
|
|
||||||
# <?xml version="1.0" encoding="TF-8"?>
|
|
||||||
# raises LookupError: unknown encoding: TF-8
|
|
||||||
raise exception.MalformedRequestBody(reason=six.text_type(e))
|
|
|
@ -1,28 +0,0 @@
|
||||||
<domain type='kvm' id='100'>
|
|
||||||
<name>i-A9B8C7D6</name>
|
|
||||||
<uuid>12a345bc-67c8-901d-2e34-56f7g89012h3</uuid>
|
|
||||||
<memory>524288</memory>
|
|
||||||
<currentMemory>524288</currentMemory>
|
|
||||||
<vcpu>1</vcpu>
|
|
||||||
<os/>
|
|
||||||
<features>
|
|
||||||
<acpi/>
|
|
||||||
</features>
|
|
||||||
<clock offset='utc'/>
|
|
||||||
<on_poweroff>destroy</on_poweroff>
|
|
||||||
<on_reboot>restart</on_reboot>
|
|
||||||
<on_crash>destroy</on_crash>
|
|
||||||
<devices>
|
|
||||||
<emulator>/usr/bin/kvm</emulator>
|
|
||||||
<disk type='file' device='disk'>
|
|
||||||
<source file='/var/lib/fakevirt/instances/i-A9B8C7D6/disk'/>
|
|
||||||
<target dev='sda' bus='scsi'/>
|
|
||||||
</disk>
|
|
||||||
<interface type='bridge'>
|
|
||||||
<mac address='a0:1b:c2:3d:4e:f5'/>
|
|
||||||
<source bridge='fakebr2000'/>
|
|
||||||
<target dev='vnet1'/>
|
|
||||||
<model type='e1000'/>
|
|
||||||
</interface>
|
|
||||||
</devices>
|
|
||||||
</domain>
|
|
|
@ -39,7 +39,6 @@ from oslotest import moxstubout
|
||||||
import six
|
import six
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from nova.api.openstack import wsgi
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova.network import manager as network_manager
|
from nova.network import manager as network_manager
|
||||||
|
@ -150,12 +149,6 @@ class skipIf(object):
|
||||||
'classes')
|
'classes')
|
||||||
|
|
||||||
|
|
||||||
class skipXmlTest(skipIf):
|
|
||||||
def __init__(self, reason):
|
|
||||||
super(skipXmlTest, self).__init__(wsgi.DISABLE_XML_V2_API,
|
|
||||||
reason)
|
|
||||||
|
|
||||||
|
|
||||||
def _patch_mock_to_raise_for_invalid_assert_calls():
|
def _patch_mock_to_raise_for_invalid_assert_calls():
|
||||||
def raise_for_invalid_assert_calls(wrapped):
|
def raise_for_invalid_assert_calls(wrapped):
|
||||||
def wrapper(_self, name):
|
def wrapper(_self, name):
|
||||||
|
|
|
@ -16,16 +16,11 @@
|
||||||
import copy
|
import copy
|
||||||
import uuid as stdlib_uuid
|
import uuid as stdlib_uuid
|
||||||
|
|
||||||
import feedparser
|
|
||||||
from lxml import etree
|
|
||||||
from oslo.serialization import jsonutils
|
from oslo.serialization import jsonutils
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute import versions
|
|
||||||
from nova.api.openstack.compute import views
|
from nova.api.openstack.compute import views
|
||||||
from nova.api.openstack import xmlutil
|
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import common
|
|
||||||
from nova.tests.unit.api.openstack import fakes
|
from nova.tests.unit.api.openstack import fakes
|
||||||
from nova.tests.unit import matchers
|
from nova.tests.unit import matchers
|
||||||
|
|
||||||
|
@ -59,10 +54,6 @@ EXP_VERSIONS = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"media-types": [
|
"media-types": [
|
||||||
{
|
|
||||||
"base": "application/xml",
|
|
||||||
"type": "application/vnd.openstack.compute+xml;version=2",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"base": "application/json",
|
"base": "application/json",
|
||||||
"type": "application/vnd.openstack.compute+json;version=2",
|
"type": "application/vnd.openstack.compute+json;version=2",
|
||||||
|
@ -161,11 +152,6 @@ class VersionsTestV20(test.NoDBTestCase):
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"media-types": [
|
"media-types": [
|
||||||
{
|
|
||||||
"base": "application/xml",
|
|
||||||
"type": "application/"
|
|
||||||
"vnd.openstack.compute+xml;version=2",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"base": "application/json",
|
"base": "application/json",
|
||||||
"type": "application/"
|
"type": "application/"
|
||||||
|
@ -189,131 +175,6 @@ class VersionsTestV20(test.NoDBTestCase):
|
||||||
res = req.get_response(fakes.wsgi_app())
|
res = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(404, res.status_int)
|
self.assertEqual(404, res.status_int)
|
||||||
|
|
||||||
@test.skipXmlTest("Nova v2 XML support is disabled")
|
|
||||||
def test_get_version_2_detail_xml(self):
|
|
||||||
req = webob.Request.blank('/v2/')
|
|
||||||
req.accept = "application/xml"
|
|
||||||
res = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(res.status_int, 200)
|
|
||||||
self.assertEqual(res.content_type, "application/xml")
|
|
||||||
|
|
||||||
version = etree.XML(res.body)
|
|
||||||
xmlutil.validate_schema(version, 'version')
|
|
||||||
|
|
||||||
expected = EXP_VERSIONS['v2.0']
|
|
||||||
self.assertTrue(version.xpath('/ns:version', namespaces=NS))
|
|
||||||
media_types = version.xpath('ns:media-types/ns:media-type',
|
|
||||||
namespaces=NS)
|
|
||||||
self.assertTrue(common.compare_media_types(media_types,
|
|
||||||
expected['media-types']))
|
|
||||||
for key in ['id', 'status', 'updated']:
|
|
||||||
self.assertEqual(version.get(key), expected[key])
|
|
||||||
links = version.xpath('atom:link', namespaces=NS)
|
|
||||||
self.assertTrue(common.compare_links(links,
|
|
||||||
[{'rel': 'self', 'href': 'http://localhost/v2/'}]
|
|
||||||
+ expected['links']))
|
|
||||||
|
|
||||||
@test.skipXmlTest("Nova v2 XML support is disabled")
|
|
||||||
def test_get_version_list_xml(self):
|
|
||||||
req = webob.Request.blank('/')
|
|
||||||
req.accept = "application/xml"
|
|
||||||
res = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(res.status_int, 200)
|
|
||||||
self.assertEqual(res.content_type, "application/xml")
|
|
||||||
|
|
||||||
root = etree.XML(res.body)
|
|
||||||
xmlutil.validate_schema(root, 'versions')
|
|
||||||
|
|
||||||
self.assertTrue(root.xpath('/ns:versions', namespaces=NS))
|
|
||||||
versions = root.xpath('ns:version', namespaces=NS)
|
|
||||||
self.assertEqual(len(versions), 2)
|
|
||||||
|
|
||||||
for i, v in enumerate(['v2.0', 'v2.1']):
|
|
||||||
version = versions[i]
|
|
||||||
expected = EXP_VERSIONS[v]
|
|
||||||
for key in ['id', 'status', 'updated']:
|
|
||||||
self.assertEqual(version.get(key), expected[key])
|
|
||||||
(link,) = version.xpath('atom:link', namespaces=NS)
|
|
||||||
self.assertTrue(common.compare_links(link,
|
|
||||||
[{'rel': 'self', 'href': 'http://localhost/%s/' % v}]))
|
|
||||||
|
|
||||||
@test.skipXmlTest("Nova v2 XML support is disabled")
|
|
||||||
def test_get_version_2_detail_atom(self):
|
|
||||||
req = webob.Request.blank('/v2/')
|
|
||||||
req.accept = "application/atom+xml"
|
|
||||||
res = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(res.status_int, 200)
|
|
||||||
self.assertEqual("application/atom+xml", res.content_type)
|
|
||||||
|
|
||||||
xmlutil.validate_schema(etree.XML(res.body), 'atom')
|
|
||||||
|
|
||||||
f = feedparser.parse(res.body)
|
|
||||||
self.assertEqual(f.feed.title, 'About This Version')
|
|
||||||
self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z')
|
|
||||||
self.assertEqual(f.feed.id, 'http://localhost/v2/')
|
|
||||||
self.assertEqual(f.feed.author, 'Rackspace')
|
|
||||||
self.assertEqual(f.feed.author_detail.href,
|
|
||||||
'http://www.rackspace.com/')
|
|
||||||
self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v2/')
|
|
||||||
self.assertEqual(f.feed.links[0]['rel'], 'self')
|
|
||||||
|
|
||||||
self.assertEqual(len(f.entries), 1)
|
|
||||||
entry = f.entries[0]
|
|
||||||
self.assertEqual(entry.id, 'http://localhost/v2/')
|
|
||||||
self.assertEqual(entry.title, 'Version v2.0')
|
|
||||||
self.assertEqual(entry.updated, '2011-01-21T11:33:21Z')
|
|
||||||
self.assertEqual(len(entry.content), 1)
|
|
||||||
self.assertEqual(entry.content[0].value,
|
|
||||||
'Version v2.0 CURRENT (2011-01-21T11:33:21Z)')
|
|
||||||
self.assertEqual(len(entry.links), 2)
|
|
||||||
self.assertEqual(entry.links[0]['href'], 'http://localhost/v2/')
|
|
||||||
self.assertEqual(entry.links[0]['rel'], 'self')
|
|
||||||
self.assertEqual(entry.links[1], {
|
|
||||||
'href': EXP_LINKS['v2.0']['html'],
|
|
||||||
'type': 'text/html',
|
|
||||||
'rel': 'describedby'})
|
|
||||||
|
|
||||||
@test.skipXmlTest("Nova v2 XML support is disabled")
|
|
||||||
def test_get_version_list_atom(self):
|
|
||||||
req = webob.Request.blank('/')
|
|
||||||
req.accept = "application/atom+xml"
|
|
||||||
res = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(res.status_int, 200)
|
|
||||||
self.assertEqual(res.content_type, "application/atom+xml")
|
|
||||||
|
|
||||||
f = feedparser.parse(res.body)
|
|
||||||
self.assertEqual(f.feed.title, 'Available API Versions')
|
|
||||||
self.assertEqual(f.feed.updated, '2013-07-23T11:33:21Z')
|
|
||||||
self.assertEqual(f.feed.id, 'http://localhost/')
|
|
||||||
self.assertEqual(f.feed.author, 'Rackspace')
|
|
||||||
self.assertEqual(f.feed.author_detail.href,
|
|
||||||
'http://www.rackspace.com/')
|
|
||||||
self.assertEqual(f.feed.links[0]['href'], 'http://localhost/')
|
|
||||||
self.assertEqual(f.feed.links[0]['rel'], 'self')
|
|
||||||
|
|
||||||
self.assertEqual(len(f.entries), 2)
|
|
||||||
entry = f.entries[0]
|
|
||||||
self.assertEqual(entry.id, 'http://localhost/v2/')
|
|
||||||
self.assertEqual(entry.title, 'Version v2.0')
|
|
||||||
self.assertEqual(entry.updated, '2011-01-21T11:33:21Z')
|
|
||||||
self.assertEqual(len(entry.content), 1)
|
|
||||||
self.assertEqual(entry.content[0].value,
|
|
||||||
'Version v2.0 CURRENT (2011-01-21T11:33:21Z)')
|
|
||||||
self.assertEqual(len(entry.links), 1)
|
|
||||||
self.assertEqual(entry.links[0]['href'], 'http://localhost/v2/')
|
|
||||||
self.assertEqual(entry.links[0]['rel'], 'self')
|
|
||||||
|
|
||||||
entry = f.entries[1]
|
|
||||||
self.assertEqual(entry.id, 'http://localhost/v2/')
|
|
||||||
self.assertEqual(entry.title, 'Version v2.1')
|
|
||||||
self.assertEqual(entry.updated, '2013-07-23T11:33:21Z')
|
|
||||||
self.assertEqual(len(entry.content), 1)
|
|
||||||
self.assertEqual(entry.content[0].value,
|
|
||||||
'Version v2.1 EXPERIMENTAL (2013-07-23T11:33:21Z)')
|
|
||||||
self.assertEqual(len(entry.links), 1)
|
|
||||||
self.assertEqual(entry.links[0]['href'], 'http://localhost/v2/')
|
|
||||||
self.assertEqual(entry.links[0]['rel'], 'self')
|
|
||||||
|
|
||||||
def test_multi_choice_image(self):
|
def test_multi_choice_image(self):
|
||||||
req = webob.Request.blank('/images/1')
|
req = webob.Request.blank('/images/1')
|
||||||
req.accept = "application/json"
|
req.accept = "application/json"
|
||||||
|
@ -333,11 +194,6 @@ class VersionsTestV20(test.NoDBTestCase):
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"media-types": [
|
"media-types": [
|
||||||
{
|
|
||||||
"base": "application/xml",
|
|
||||||
"type": "application/vnd.openstack.compute+xml"
|
|
||||||
";version=2"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"base": "application/json",
|
"base": "application/json",
|
||||||
"type": "application/vnd.openstack.compute+json"
|
"type": "application/vnd.openstack.compute+json"
|
||||||
|
@ -367,47 +223,6 @@ class VersionsTestV20(test.NoDBTestCase):
|
||||||
self.assertThat(jsonutils.loads(res.body),
|
self.assertThat(jsonutils.loads(res.body),
|
||||||
matchers.DictMatches(expected))
|
matchers.DictMatches(expected))
|
||||||
|
|
||||||
@test.skipXmlTest("Nova v2 XML support is disabled")
|
|
||||||
def test_multi_choice_image_xml(self):
|
|
||||||
req = webob.Request.blank('/images/1')
|
|
||||||
req.accept = "application/xml"
|
|
||||||
res = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(res.status_int, 300)
|
|
||||||
self.assertEqual(res.content_type, "application/xml")
|
|
||||||
|
|
||||||
root = etree.XML(res.body)
|
|
||||||
self.assertTrue(root.xpath('/ns:choices', namespaces=NS))
|
|
||||||
versions = root.xpath('ns:version', namespaces=NS)
|
|
||||||
self.assertEqual(len(versions), 2)
|
|
||||||
|
|
||||||
version = versions[0]
|
|
||||||
self.assertEqual(version.get('id'), 'v2.0')
|
|
||||||
self.assertEqual(version.get('status'), 'CURRENT')
|
|
||||||
media_types = version.xpath('ns:media-types/ns:media-type',
|
|
||||||
namespaces=NS)
|
|
||||||
self.assertTrue(common.
|
|
||||||
compare_media_types(media_types,
|
|
||||||
EXP_VERSIONS['v2.0']['media-types']
|
|
||||||
))
|
|
||||||
|
|
||||||
links = version.xpath('atom:link', namespaces=NS)
|
|
||||||
self.assertTrue(common.compare_links(links,
|
|
||||||
[{'rel': 'self', 'href': 'http://localhost/v2/images/1'}]))
|
|
||||||
|
|
||||||
version = versions[1]
|
|
||||||
self.assertEqual(version.get('id'), 'v2.1')
|
|
||||||
self.assertEqual(version.get('status'), 'EXPERIMENTAL')
|
|
||||||
media_types = version.xpath('ns:media-types/ns:media-type',
|
|
||||||
namespaces=NS)
|
|
||||||
self.assertTrue(common.
|
|
||||||
compare_media_types(media_types,
|
|
||||||
EXP_VERSIONS['v2.1']['media-types']
|
|
||||||
))
|
|
||||||
|
|
||||||
links = version.xpath('atom:link', namespaces=NS)
|
|
||||||
self.assertTrue(common.compare_links(links,
|
|
||||||
[{'rel': 'self', 'href': 'http://localhost/v2/images/1'}]))
|
|
||||||
|
|
||||||
def test_multi_choice_server_atom(self):
|
def test_multi_choice_server_atom(self):
|
||||||
"""Make sure multi choice responses do not have content-type
|
"""Make sure multi choice responses do not have content-type
|
||||||
application/atom+xml (should use default of json)
|
application/atom+xml (should use default of json)
|
||||||
|
@ -438,11 +253,6 @@ class VersionsTestV20(test.NoDBTestCase):
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"media-types": [
|
"media-types": [
|
||||||
{
|
|
||||||
"base": "application/xml",
|
|
||||||
"type": "application/vnd.openstack.compute+xml"
|
|
||||||
";version=2"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"base": "application/json",
|
"base": "application/json",
|
||||||
"type": "application/vnd.openstack.compute+json"
|
"type": "application/vnd.openstack.compute+json"
|
||||||
|
@ -537,208 +347,6 @@ class VersionsViewBuilderTests(test.NoDBTestCase):
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
|
||||||
class VersionsSerializerTests(test.NoDBTestCase):
|
|
||||||
def test_versions_list_xml_serializer(self):
|
|
||||||
versions_data = {
|
|
||||||
'versions': [
|
|
||||||
{
|
|
||||||
"id": "2.7",
|
|
||||||
"updated": "2011-07-18T11:30:00Z",
|
|
||||||
"status": "DEPRECATED",
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"rel": "self",
|
|
||||||
"href": "http://test/v2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer = versions.VersionsTemplate()
|
|
||||||
response = serializer.serialize(versions_data)
|
|
||||||
|
|
||||||
root = etree.XML(response)
|
|
||||||
xmlutil.validate_schema(root, 'versions')
|
|
||||||
|
|
||||||
self.assertTrue(root.xpath('/ns:versions', namespaces=NS))
|
|
||||||
version_elems = root.xpath('ns:version', namespaces=NS)
|
|
||||||
self.assertEqual(len(version_elems), 1)
|
|
||||||
version = version_elems[0]
|
|
||||||
self.assertEqual(version.get('id'), versions_data['versions'][0]['id'])
|
|
||||||
self.assertEqual(version.get('status'),
|
|
||||||
versions_data['versions'][0]['status'])
|
|
||||||
|
|
||||||
(link,) = version.xpath('atom:link', namespaces=NS)
|
|
||||||
self.assertTrue(common.compare_links(link, [{
|
|
||||||
'rel': 'self',
|
|
||||||
'href': 'http://test/v2',
|
|
||||||
'type': 'application/atom+xml'}]))
|
|
||||||
|
|
||||||
def test_versions_multi_xml_serializer(self):
|
|
||||||
versions_data = {
|
|
||||||
'choices': [
|
|
||||||
{
|
|
||||||
"id": "2.7",
|
|
||||||
"updated": "2011-07-18T11:30:00Z",
|
|
||||||
"status": "DEPRECATED",
|
|
||||||
"media-types": EXP_VERSIONS['v2.0']['media-types'],
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"rel": "self",
|
|
||||||
"href": "http://test/v2/images",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer = versions.ChoicesTemplate()
|
|
||||||
response = serializer.serialize(versions_data)
|
|
||||||
|
|
||||||
root = etree.XML(response)
|
|
||||||
self.assertTrue(root.xpath('/ns:choices', namespaces=NS))
|
|
||||||
(version,) = root.xpath('ns:version', namespaces=NS)
|
|
||||||
self.assertEqual(version.get('id'), versions_data['choices'][0]['id'])
|
|
||||||
self.assertEqual(version.get('status'),
|
|
||||||
versions_data['choices'][0]['status'])
|
|
||||||
|
|
||||||
media_types = list(version)[0]
|
|
||||||
self.assertEqual(media_types.tag.split('}')[1], "media-types")
|
|
||||||
|
|
||||||
media_types = version.xpath('ns:media-types/ns:media-type',
|
|
||||||
namespaces=NS)
|
|
||||||
self.assertTrue(common.compare_media_types(media_types,
|
|
||||||
versions_data['choices'][0]['media-types']))
|
|
||||||
|
|
||||||
(link,) = version.xpath('atom:link', namespaces=NS)
|
|
||||||
self.assertTrue(common.compare_links(link,
|
|
||||||
versions_data['choices'][0]['links']))
|
|
||||||
|
|
||||||
def test_versions_list_atom_serializer(self):
|
|
||||||
versions_data = {
|
|
||||||
'versions': [
|
|
||||||
{
|
|
||||||
"id": "2.9.8",
|
|
||||||
"updated": "2011-07-20T11:40:00Z",
|
|
||||||
"status": "CURRENT",
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"rel": "self",
|
|
||||||
"href": "http://test/2.9.8",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer = versions.VersionsAtomSerializer()
|
|
||||||
response = serializer.serialize(versions_data)
|
|
||||||
f = feedparser.parse(response)
|
|
||||||
|
|
||||||
self.assertEqual(f.feed.title, 'Available API Versions')
|
|
||||||
self.assertEqual(f.feed.updated, '2011-07-20T11:40:00Z')
|
|
||||||
self.assertEqual(f.feed.id, 'http://test/')
|
|
||||||
self.assertEqual(f.feed.author, 'Rackspace')
|
|
||||||
self.assertEqual(f.feed.author_detail.href,
|
|
||||||
'http://www.rackspace.com/')
|
|
||||||
self.assertEqual(f.feed.links[0]['href'], 'http://test/')
|
|
||||||
self.assertEqual(f.feed.links[0]['rel'], 'self')
|
|
||||||
|
|
||||||
self.assertEqual(len(f.entries), 1)
|
|
||||||
entry = f.entries[0]
|
|
||||||
self.assertEqual(entry.id, 'http://test/2.9.8')
|
|
||||||
self.assertEqual(entry.title, 'Version 2.9.8')
|
|
||||||
self.assertEqual(entry.updated, '2011-07-20T11:40:00Z')
|
|
||||||
self.assertEqual(len(entry.content), 1)
|
|
||||||
self.assertEqual(entry.content[0].value,
|
|
||||||
'Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)')
|
|
||||||
self.assertEqual(len(entry.links), 1)
|
|
||||||
self.assertEqual(entry.links[0]['href'], 'http://test/2.9.8')
|
|
||||||
self.assertEqual(entry.links[0]['rel'], 'self')
|
|
||||||
|
|
||||||
def test_version_detail_atom_serializer(self):
|
|
||||||
versions_data = {
|
|
||||||
"version": {
|
|
||||||
"id": "v2.0",
|
|
||||||
"status": "CURRENT",
|
|
||||||
"updated": "2011-01-21T11:33:21Z",
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"rel": "self",
|
|
||||||
"href": "http://localhost/v2/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rel": "describedby",
|
|
||||||
"type": "text/html",
|
|
||||||
"href": EXP_LINKS['v2.0']['html'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"media-types": [
|
|
||||||
{
|
|
||||||
"base": "application/xml",
|
|
||||||
"type": "application/vnd.openstack.compute+xml"
|
|
||||||
";version=2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"base": "application/json",
|
|
||||||
"type": "application/vnd.openstack.compute+json"
|
|
||||||
";version=2",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer = versions.VersionAtomSerializer()
|
|
||||||
response = serializer.serialize(versions_data)
|
|
||||||
f = feedparser.parse(response)
|
|
||||||
|
|
||||||
self.assertEqual(f.feed.title, 'About This Version')
|
|
||||||
self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z')
|
|
||||||
self.assertEqual(f.feed.id, 'http://localhost/v2/')
|
|
||||||
self.assertEqual(f.feed.author, 'Rackspace')
|
|
||||||
self.assertEqual(f.feed.author_detail.href,
|
|
||||||
'http://www.rackspace.com/')
|
|
||||||
self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v2/')
|
|
||||||
self.assertEqual(f.feed.links[0]['rel'], 'self')
|
|
||||||
|
|
||||||
self.assertEqual(len(f.entries), 1)
|
|
||||||
entry = f.entries[0]
|
|
||||||
self.assertEqual(entry.id, 'http://localhost/v2/')
|
|
||||||
self.assertEqual(entry.title, 'Version v2.0')
|
|
||||||
self.assertEqual(entry.updated, '2011-01-21T11:33:21Z')
|
|
||||||
self.assertEqual(len(entry.content), 1)
|
|
||||||
self.assertEqual(entry.content[0].value,
|
|
||||||
'Version v2.0 CURRENT (2011-01-21T11:33:21Z)')
|
|
||||||
self.assertEqual(len(entry.links), 2)
|
|
||||||
self.assertEqual(entry.links[0]['href'], 'http://localhost/v2/')
|
|
||||||
self.assertEqual(entry.links[0]['rel'], 'self')
|
|
||||||
self.assertEqual(entry.links[1], {
|
|
||||||
'rel': 'describedby',
|
|
||||||
'type': 'text/html',
|
|
||||||
'href': EXP_LINKS['v2.0']['html']})
|
|
||||||
|
|
||||||
def test_multi_choice_image_with_body(self):
|
|
||||||
req = webob.Request.blank('/images/1')
|
|
||||||
req.accept = "application/json"
|
|
||||||
req.method = 'POST'
|
|
||||||
req.content_type = "application/json"
|
|
||||||
req.body = "{\"foo\": \"bar\"}"
|
|
||||||
res = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(300, res.status_int)
|
|
||||||
self.assertEqual("application/json", res.content_type)
|
|
||||||
|
|
||||||
def test_get_version_list_with_body(self):
|
|
||||||
req = webob.Request.blank('/')
|
|
||||||
req.accept = "application/json"
|
|
||||||
req.method = 'POST'
|
|
||||||
req.content_type = "application/json"
|
|
||||||
req.body = "{\"foo\": \"bar\"}"
|
|
||||||
res = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(200, res.status_int)
|
|
||||||
self.assertEqual("application/json", res.content_type)
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE(oomichi): Now version API of v2.0 covers "/"(root).
|
# NOTE(oomichi): Now version API of v2.0 covers "/"(root).
|
||||||
# So this class tests "/v2.1" only for v2.1 API.
|
# So this class tests "/v2.1" only for v2.1 API.
|
||||||
class VersionsTestV21(test.NoDBTestCase):
|
class VersionsTestV21(test.NoDBTestCase):
|
||||||
|
|
Loading…
Reference in New Issue