refactoring wsgi to separate controller/serialization/deserialization logic; creating osapi-specific module
This commit is contained in:
		@@ -26,7 +26,7 @@ import webob.exc
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from nova import flags
 | 
					from nova import flags
 | 
				
			||||||
from nova import log as logging
 | 
					from nova import log as logging
 | 
				
			||||||
from nova import wsgi
 | 
					from nova import wsgi as base_wsgi
 | 
				
			||||||
from nova.api.openstack import accounts
 | 
					from nova.api.openstack import accounts
 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
from nova.api.openstack import backup_schedules
 | 
					from nova.api.openstack import backup_schedules
 | 
				
			||||||
@@ -40,6 +40,7 @@ from nova.api.openstack import servers
 | 
				
			|||||||
from nova.api.openstack import server_metadata
 | 
					from nova.api.openstack import server_metadata
 | 
				
			||||||
from nova.api.openstack import shared_ip_groups
 | 
					from nova.api.openstack import shared_ip_groups
 | 
				
			||||||
from nova.api.openstack import users
 | 
					from nova.api.openstack import users
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
from nova.api.openstack import zones
 | 
					from nova.api.openstack import zones
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -50,7 +51,7 @@ flags.DEFINE_bool('allow_admin_api',
 | 
				
			|||||||
    'When True, this API service will accept admin operations.')
 | 
					    'When True, this API service will accept admin operations.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FaultWrapper(wsgi.Middleware):
 | 
					class FaultWrapper(base_wsgi.Middleware):
 | 
				
			||||||
    """Calls down the middleware stack, making exceptions into faults."""
 | 
					    """Calls down the middleware stack, making exceptions into faults."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @webob.dec.wsgify(RequestClass=wsgi.Request)
 | 
					    @webob.dec.wsgify(RequestClass=wsgi.Request)
 | 
				
			||||||
@@ -63,7 +64,7 @@ class FaultWrapper(wsgi.Middleware):
 | 
				
			|||||||
            return faults.Fault(exc)
 | 
					            return faults.Fault(exc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class APIRouter(wsgi.Router):
 | 
					class APIRouter(base_wsgi.Router):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Routes requests on the OpenStack API to the appropriate controller
 | 
					    Routes requests on the OpenStack API to the appropriate controller
 | 
				
			||||||
    and method.
 | 
					    and method.
 | 
				
			||||||
@@ -97,18 +98,20 @@ class APIRouter(wsgi.Router):
 | 
				
			|||||||
            server_members['reset_network'] = 'POST'
 | 
					            server_members['reset_network'] = 'POST'
 | 
				
			||||||
            server_members['inject_network_info'] = 'POST'
 | 
					            server_members['inject_network_info'] = 'POST'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            mapper.resource("zone", "zones", controller=zones.Controller(),
 | 
					            mapper.resource("zone", "zones",
 | 
				
			||||||
 | 
					                        controller=zones.resource_factory(),
 | 
				
			||||||
                        collection={'detail': 'GET', 'info': 'GET'}),
 | 
					                        collection={'detail': 'GET', 'info': 'GET'}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            mapper.resource("user", "users", controller=users.Controller(),
 | 
					            mapper.resource("user", "users",
 | 
				
			||||||
 | 
					                        controller=users.resource_factory(),
 | 
				
			||||||
                        collection={'detail': 'GET'})
 | 
					                        collection={'detail': 'GET'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            mapper.resource("account", "accounts",
 | 
					            mapper.resource("account", "accounts",
 | 
				
			||||||
                            controller=accounts.Controller(),
 | 
					                            controller=accounts.resource_factory(),
 | 
				
			||||||
                            collection={'detail': 'GET'})
 | 
					                            collection={'detail': 'GET'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("console", "consoles",
 | 
					        mapper.resource("console", "consoles",
 | 
				
			||||||
                        controller=consoles.Controller(),
 | 
					                        controller=consoles.resource_factory(),
 | 
				
			||||||
                        parent_resource=dict(member_name='server',
 | 
					                        parent_resource=dict(member_name='server',
 | 
				
			||||||
                        collection_name='servers'))
 | 
					                        collection_name='servers'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -121,31 +124,31 @@ class APIRouterV10(APIRouter):
 | 
				
			|||||||
    def _setup_routes(self, mapper):
 | 
					    def _setup_routes(self, mapper):
 | 
				
			||||||
        super(APIRouterV10, self)._setup_routes(mapper)
 | 
					        super(APIRouterV10, self)._setup_routes(mapper)
 | 
				
			||||||
        mapper.resource("server", "servers",
 | 
					        mapper.resource("server", "servers",
 | 
				
			||||||
                        controller=servers.ControllerV10(),
 | 
					                        controller=servers.resource_factory('1.0'),
 | 
				
			||||||
                        collection={'detail': 'GET'},
 | 
					                        collection={'detail': 'GET'},
 | 
				
			||||||
                        member=self.server_members)
 | 
					                        member=self.server_members)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("image", "images",
 | 
					        mapper.resource("image", "images",
 | 
				
			||||||
                        controller=images.ControllerV10(),
 | 
					                        controller=images.resource_factory('1.0'),
 | 
				
			||||||
                        collection={'detail': 'GET'})
 | 
					                        collection={'detail': 'GET'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("flavor", "flavors",
 | 
					        mapper.resource("flavor", "flavors",
 | 
				
			||||||
                        controller=flavors.ControllerV10(),
 | 
					                        controller=flavors.resource_factory('1.0'),
 | 
				
			||||||
                        collection={'detail': 'GET'})
 | 
					                        collection={'detail': 'GET'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("shared_ip_group", "shared_ip_groups",
 | 
					        mapper.resource("shared_ip_group", "shared_ip_groups",
 | 
				
			||||||
                        collection={'detail': 'GET'},
 | 
					                        collection={'detail': 'GET'},
 | 
				
			||||||
                        controller=shared_ip_groups.Controller())
 | 
					                        controller=shared_ip_groups.resource_factory())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("backup_schedule", "backup_schedule",
 | 
					        mapper.resource("backup_schedule", "backup_schedule",
 | 
				
			||||||
                        controller=backup_schedules.Controller(),
 | 
					                        controller=backup_schedules.resource_factory(),
 | 
				
			||||||
                        parent_resource=dict(member_name='server',
 | 
					                        parent_resource=dict(member_name='server',
 | 
				
			||||||
                        collection_name='servers'))
 | 
					                        collection_name='servers'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("limit", "limits",
 | 
					        mapper.resource("limit", "limits",
 | 
				
			||||||
                        controller=limits.LimitsControllerV10())
 | 
					                        controller=limits.resource_factory('1.0'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("ip", "ips", controller=ips.Controller(),
 | 
					        mapper.resource("ip", "ips", controller=ips.resource_factory(),
 | 
				
			||||||
                        collection=dict(public='GET', private='GET'),
 | 
					                        collection=dict(public='GET', private='GET'),
 | 
				
			||||||
                        parent_resource=dict(member_name='server',
 | 
					                        parent_resource=dict(member_name='server',
 | 
				
			||||||
                                             collection_name='servers'))
 | 
					                                             collection_name='servers'))
 | 
				
			||||||
@@ -157,27 +160,27 @@ class APIRouterV11(APIRouter):
 | 
				
			|||||||
    def _setup_routes(self, mapper):
 | 
					    def _setup_routes(self, mapper):
 | 
				
			||||||
        super(APIRouterV11, self)._setup_routes(mapper)
 | 
					        super(APIRouterV11, self)._setup_routes(mapper)
 | 
				
			||||||
        mapper.resource("server", "servers",
 | 
					        mapper.resource("server", "servers",
 | 
				
			||||||
                        controller=servers.ControllerV11(),
 | 
					                        controller=servers.resource_factory('1.1'),
 | 
				
			||||||
                        collection={'detail': 'GET'},
 | 
					                        collection={'detail': 'GET'},
 | 
				
			||||||
                        member=self.server_members)
 | 
					                        member=self.server_members)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("image", "images",
 | 
					        mapper.resource("image", "images",
 | 
				
			||||||
                        controller=images.ControllerV11(),
 | 
					                        controller=images.resource_factory('1.1'),
 | 
				
			||||||
                        collection={'detail': 'GET'})
 | 
					                        collection={'detail': 'GET'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("image_meta", "meta",
 | 
					        mapper.resource("image_meta", "meta",
 | 
				
			||||||
                        controller=image_metadata.Controller(),
 | 
					                        controller=image_metadata.resource_factory(),
 | 
				
			||||||
                        parent_resource=dict(member_name='image',
 | 
					                        parent_resource=dict(member_name='image',
 | 
				
			||||||
                        collection_name='images'))
 | 
					                        collection_name='images'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("server_meta", "meta",
 | 
					        mapper.resource("server_meta", "meta",
 | 
				
			||||||
                        controller=server_metadata.Controller(),
 | 
					                        controller=server_metadata.resource_factory(),
 | 
				
			||||||
                        parent_resource=dict(member_name='server',
 | 
					                        parent_resource=dict(member_name='server',
 | 
				
			||||||
                        collection_name='servers'))
 | 
					                        collection_name='servers'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("flavor", "flavors",
 | 
					        mapper.resource("flavor", "flavors",
 | 
				
			||||||
                        controller=flavors.ControllerV11(),
 | 
					                        controller=flavors.resource_factory('1.1'),
 | 
				
			||||||
                        collection={'detail': 'GET'})
 | 
					                        collection={'detail': 'GET'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mapper.resource("limit", "limits",
 | 
					        mapper.resource("limit", "limits",
 | 
				
			||||||
                        controller=limits.LimitsControllerV11())
 | 
					                        controller=limits.resource_factory('1.1'))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,8 +20,9 @@ from nova import flags
 | 
				
			|||||||
from nova import log as logging
 | 
					from nova import log as logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from nova.auth import manager
 | 
					from nova.auth import manager
 | 
				
			||||||
from nova.api.openstack import common
 | 
					 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FLAGS = flags.FLAGS
 | 
					FLAGS = flags.FLAGS
 | 
				
			||||||
LOG = logging.getLogger('nova.api.openstack')
 | 
					LOG = logging.getLogger('nova.api.openstack')
 | 
				
			||||||
@@ -34,12 +35,7 @@ def _translate_keys(account):
 | 
				
			|||||||
                manager=account.project_manager_id)
 | 
					                manager=account.project_manager_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            "attributes": {
 | 
					 | 
				
			||||||
                "account": ["id", "name", "description", "manager"]}}}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.manager = manager.AuthManager()
 | 
					        self.manager = manager.AuthManager()
 | 
				
			||||||
@@ -66,20 +62,33 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        self.manager.delete_project(id)
 | 
					        self.manager.delete_project(id)
 | 
				
			||||||
        return {}
 | 
					        return {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req):
 | 
					    def create(self, req, body):
 | 
				
			||||||
        """We use update with create-or-update semantics
 | 
					        """We use update with create-or-update semantics
 | 
				
			||||||
           because the id comes from an external source"""
 | 
					           because the id comes from an external source"""
 | 
				
			||||||
        raise faults.Fault(webob.exc.HTTPNotImplemented())
 | 
					        raise faults.Fault(webob.exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, req, id):
 | 
					    def update(self, req, id, body):
 | 
				
			||||||
        """This is really create or update."""
 | 
					        """This is really create or update."""
 | 
				
			||||||
        self._check_admin(req.environ['nova.context'])
 | 
					        self._check_admin(req.environ['nova.context'])
 | 
				
			||||||
        env = self._deserialize(req.body, req.get_content_type())
 | 
					        description = body['account'].get('description')
 | 
				
			||||||
        description = env['account'].get('description')
 | 
					        manager = body['account'].get('manager')
 | 
				
			||||||
        manager = env['account'].get('manager')
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            account = self.manager.get_project(id)
 | 
					            account = self.manager.get_project(id)
 | 
				
			||||||
            self.manager.modify_project(id, manager, description)
 | 
					            self.manager.modify_project(id, manager, description)
 | 
				
			||||||
        except exception.NotFound:
 | 
					        except exception.NotFound:
 | 
				
			||||||
            account = self.manager.create_project(id, manager, description)
 | 
					            account = self.manager.create_project(id, manager, description)
 | 
				
			||||||
        return dict(account=_translate_keys(account))
 | 
					        return dict(account=_translate_keys(account))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        "attributes": {
 | 
				
			||||||
 | 
					            "account": ["id", "name", "description", "manager"],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(metadata=metadata),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller(), serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,9 +19,8 @@ import time
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from webob import exc
 | 
					from webob import exc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from nova.api.openstack import common
 | 
					 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
import nova.image.service
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _translate_keys(inst):
 | 
					def _translate_keys(inst):
 | 
				
			||||||
@@ -29,14 +28,9 @@ def _translate_keys(inst):
 | 
				
			|||||||
    return dict(backupSchedule=inst)
 | 
					    return dict(backupSchedule=inst)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
    """ The backup schedule API controller for the Openstack API """
 | 
					    """ The backup schedule API controller for the Openstack API """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            'attributes': {
 | 
					 | 
				
			||||||
                'backupSchedule': []}}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,7 +42,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        """ Returns a single backup schedule for a given instance """
 | 
					        """ Returns a single backup schedule for a given instance """
 | 
				
			||||||
        return faults.Fault(exc.HTTPNotImplemented())
 | 
					        return faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req, server_id):
 | 
					    def create(self, req, server_id, body):
 | 
				
			||||||
        """ No actual update method required, since the existing API allows
 | 
					        """ No actual update method required, since the existing API allows
 | 
				
			||||||
        both create and update through a POST """
 | 
					        both create and update through a POST """
 | 
				
			||||||
        return faults.Fault(exc.HTTPNotImplemented())
 | 
					        return faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
@@ -56,3 +50,18 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
    def delete(self, req, server_id, id):
 | 
					    def delete(self, req, server_id, id):
 | 
				
			||||||
        """ Deletes an existing backup schedule """
 | 
					        """ Deletes an existing backup schedule """
 | 
				
			||||||
        return faults.Fault(exc.HTTPNotImplemented())
 | 
					        return faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        'attributes': {
 | 
				
			||||||
 | 
					            'backupSchedule': [],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(xmlns=wsgi.XMLNS_V10,
 | 
				
			||||||
 | 
					                                              metadata=metadata),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller(), serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,8 +19,8 @@ from webob import exc
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from nova import console
 | 
					from nova import console
 | 
				
			||||||
from nova import exception
 | 
					from nova import exception
 | 
				
			||||||
from nova.api.openstack import common
 | 
					 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _translate_keys(cons):
 | 
					def _translate_keys(cons):
 | 
				
			||||||
@@ -43,14 +43,9 @@ def _translate_detail_keys(cons):
 | 
				
			|||||||
    return dict(console=info)
 | 
					    return dict(console=info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
    """The Consoles Controller for the Openstack API"""
 | 
					    """The Consoles Controller for the Openstack API"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            'attributes': {
 | 
					 | 
				
			||||||
                'console': []}}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.console_api = console.API()
 | 
					        self.console_api = console.API()
 | 
				
			||||||
        super(Controller, self).__init__()
 | 
					        super(Controller, self).__init__()
 | 
				
			||||||
@@ -63,9 +58,8 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        return dict(consoles=[_translate_keys(console)
 | 
					        return dict(consoles=[_translate_keys(console)
 | 
				
			||||||
                              for console in consoles])
 | 
					                              for console in consoles])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req, server_id):
 | 
					    def create(self, req, server_id, body):
 | 
				
			||||||
        """Creates a new console"""
 | 
					        """Creates a new console"""
 | 
				
			||||||
        #info = self._deserialize(req.body, req.get_content_type())
 | 
					 | 
				
			||||||
        self.console_api.create_console(
 | 
					        self.console_api.create_console(
 | 
				
			||||||
                                req.environ['nova.context'],
 | 
					                                req.environ['nova.context'],
 | 
				
			||||||
                                int(server_id))
 | 
					                                int(server_id))
 | 
				
			||||||
@@ -94,3 +88,17 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        except exception.NotFound:
 | 
					        except exception.NotFound:
 | 
				
			||||||
            return faults.Fault(exc.HTTPNotFound())
 | 
					            return faults.Fault(exc.HTTPNotFound())
 | 
				
			||||||
        return exc.HTTPAccepted()
 | 
					        return exc.HTTPAccepted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        'attributes': {
 | 
				
			||||||
 | 
					            'console': [],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(metadata=metadata),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller(), serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,22 +19,13 @@ import webob
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from nova import db
 | 
					from nova import db
 | 
				
			||||||
from nova import exception
 | 
					from nova import exception
 | 
				
			||||||
from nova.api.openstack import common
 | 
					 | 
				
			||||||
from nova.api.openstack import views
 | 
					from nova.api.openstack import views
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
    """Flavor controller for the OpenStack API."""
 | 
					    """Flavor controller for the OpenStack API."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            "attributes": {
 | 
					 | 
				
			||||||
                "flavor": ["id", "name", "ram", "disk"],
 | 
					 | 
				
			||||||
                "link": ["rel", "type", "href"],
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def index(self, req):
 | 
					    def index(self, req):
 | 
				
			||||||
        """Return all flavors in brief."""
 | 
					        """Return all flavors in brief."""
 | 
				
			||||||
        items = self._get_flavors(req, is_detail=False)
 | 
					        items = self._get_flavors(req, is_detail=False)
 | 
				
			||||||
@@ -71,14 +62,31 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ControllerV10(Controller):
 | 
					class ControllerV10(Controller):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_view_builder(self, req):
 | 
					    def _get_view_builder(self, req):
 | 
				
			||||||
        return views.flavors.ViewBuilder()
 | 
					        return views.flavors.ViewBuilder()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ControllerV11(Controller):
 | 
					class ControllerV11(Controller):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_view_builder(self, req):
 | 
					    def _get_view_builder(self, req):
 | 
				
			||||||
        base_url = req.application_url
 | 
					        base_url = req.application_url
 | 
				
			||||||
        return views.flavors.ViewBuilderV11(base_url)
 | 
					        return views.flavors.ViewBuilderV11(base_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_default_xmlns(self, req):
 | 
					
 | 
				
			||||||
        return common.XML_NS_V11
 | 
					def resource_factory(version='1.0'):
 | 
				
			||||||
 | 
					    controller = {
 | 
				
			||||||
 | 
					        '1.0': ControllerV10,
 | 
				
			||||||
 | 
					        '1.1': ControllerV11,
 | 
				
			||||||
 | 
					    }[version]()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    xmlns = {
 | 
				
			||||||
 | 
					        '1.0': wsgi.XMLNS_V10,
 | 
				
			||||||
 | 
					        '1.1': wsgi.XMLNS_V11,
 | 
				
			||||||
 | 
					    }[version]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(xmlns=xmlns),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(controller, serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,19 +21,18 @@ from nova import flags
 | 
				
			|||||||
from nova import quota
 | 
					from nova import quota
 | 
				
			||||||
from nova import utils
 | 
					from nova import utils
 | 
				
			||||||
from nova import wsgi
 | 
					from nova import wsgi
 | 
				
			||||||
from nova.api.openstack import common
 | 
					 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FLAGS = flags.FLAGS
 | 
					FLAGS = flags.FLAGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
    """The image metadata API controller for the Openstack API"""
 | 
					    """The image metadata API controller for the Openstack API"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.image_service = utils.import_object(FLAGS.image_service)
 | 
					        self.image_service = utils.import_object(FLAGS.image_service)
 | 
				
			||||||
        super(Controller, self).__init__()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_metadata(self, context, image_id, image=None):
 | 
					    def _get_metadata(self, context, image_id, image=None):
 | 
				
			||||||
        if not image:
 | 
					        if not image:
 | 
				
			||||||
@@ -64,9 +63,8 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return faults.Fault(exc.HTTPNotFound())
 | 
					            return faults.Fault(exc.HTTPNotFound())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req, image_id):
 | 
					    def create(self, req, image_id, body):
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        body = self._deserialize(req.body, req.get_content_type())
 | 
					 | 
				
			||||||
        img = self.image_service.show(context, image_id)
 | 
					        img = self.image_service.show(context, image_id)
 | 
				
			||||||
        metadata = self._get_metadata(context, image_id, img)
 | 
					        metadata = self._get_metadata(context, image_id, img)
 | 
				
			||||||
        if 'metadata' in body:
 | 
					        if 'metadata' in body:
 | 
				
			||||||
@@ -77,9 +75,8 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        self.image_service.update(context, image_id, img, None)
 | 
					        self.image_service.update(context, image_id, img, None)
 | 
				
			||||||
        return dict(metadata=metadata)
 | 
					        return dict(metadata=metadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, req, image_id, id):
 | 
					    def update(self, req, image_id, id, body):
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        body = self._deserialize(req.body, req.get_content_type())
 | 
					 | 
				
			||||||
        if not id in body:
 | 
					        if not id in body:
 | 
				
			||||||
            expl = _('Request body and URI mismatch')
 | 
					            expl = _('Request body and URI mismatch')
 | 
				
			||||||
            raise exc.HTTPBadRequest(explanation=expl)
 | 
					            raise exc.HTTPBadRequest(explanation=expl)
 | 
				
			||||||
@@ -104,3 +101,11 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        metadata.pop(id)
 | 
					        metadata.pop(id)
 | 
				
			||||||
        img['properties'] = metadata
 | 
					        img['properties'] = metadata
 | 
				
			||||||
        self.image_service.update(context, image_id, img, None)
 | 
					        self.image_service.update(context, image_id, img, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(xmlns=wsgi.XMLNS_V11),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller(), serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,25 +23,16 @@ from nova import utils
 | 
				
			|||||||
from nova.api.openstack import common
 | 
					from nova.api.openstack import common
 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
from nova.api.openstack.views import images as images_view
 | 
					from nova.api.openstack.views import images as images_view
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOG = log.getLogger('nova.api.openstack.images')
 | 
					LOG = log.getLogger('nova.api.openstack.images')
 | 
				
			||||||
FLAGS = flags.FLAGS
 | 
					FLAGS = flags.FLAGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
    """Base `wsgi.Controller` for retrieving/displaying images."""
 | 
					    """Base `wsgi.Controller` for retrieving/displaying images."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            "attributes": {
 | 
					 | 
				
			||||||
                "image": ["id", "name", "updated", "created", "status",
 | 
					 | 
				
			||||||
                          "serverId", "progress"],
 | 
					 | 
				
			||||||
                "link": ["rel", "type", "href"],
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, image_service=None, compute_service=None):
 | 
					    def __init__(self, image_service=None, compute_service=None):
 | 
				
			||||||
        """Initialize new `ImageController`.
 | 
					        """Initialize new `ImageController`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -153,3 +144,30 @@ class ControllerV11(Controller):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def get_default_xmlns(self, req):
 | 
					    def get_default_xmlns(self, req):
 | 
				
			||||||
        return common.XML_NS_V11
 | 
					        return common.XML_NS_V11
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory(version='1.0'):
 | 
				
			||||||
 | 
					    controller = {
 | 
				
			||||||
 | 
					        '1.0': ControllerV10,
 | 
				
			||||||
 | 
					        '1.1': ControllerV11,
 | 
				
			||||||
 | 
					    }[version]()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    xmlns = {
 | 
				
			||||||
 | 
					        '1.0': wsgi.XMLNS_V10,
 | 
				
			||||||
 | 
					        '1.1': wsgi.XMLNS_V11,
 | 
				
			||||||
 | 
					    }[version]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        "attributes": {
 | 
				
			||||||
 | 
					            "image": ["id", "name", "updated", "created", "status",
 | 
				
			||||||
 | 
					                      "serverId", "progress"],
 | 
				
			||||||
 | 
					            "link": ["rel", "type", "href"],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(xmlns=xmlns,
 | 
				
			||||||
 | 
					                                              metadata=metadata),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(controller, serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,23 +20,14 @@ import time
 | 
				
			|||||||
from webob import exc
 | 
					from webob import exc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import nova
 | 
					import nova
 | 
				
			||||||
import nova.api.openstack.views.addresses
 | 
					 | 
				
			||||||
from nova.api.openstack import common
 | 
					 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
 | 
					import nova.api.openstack.views.addresses
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
    """The servers addresses API controller for the Openstack API."""
 | 
					    """The servers addresses API controller for the Openstack API."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            'list_collections': {
 | 
					 | 
				
			||||||
                'public':  {'item_name': 'ip', 'item_key': 'addr'},
 | 
					 | 
				
			||||||
                'private': {'item_name': 'ip', 'item_key': 'addr'},
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.compute_api = nova.compute.API()
 | 
					        self.compute_api = nova.compute.API()
 | 
				
			||||||
        self.builder = nova.api.openstack.views.addresses.ViewBuilderV10()
 | 
					        self.builder = nova.api.openstack.views.addresses.ViewBuilderV10()
 | 
				
			||||||
@@ -65,8 +56,24 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
    def show(self, req, server_id, id):
 | 
					    def show(self, req, server_id, id):
 | 
				
			||||||
        return faults.Fault(exc.HTTPNotImplemented())
 | 
					        return faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req, server_id):
 | 
					    def create(self, req, server_id, body):
 | 
				
			||||||
        return faults.Fault(exc.HTTPNotImplemented())
 | 
					        return faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def delete(self, req, server_id, id):
 | 
					    def delete(self, req, server_id, id):
 | 
				
			||||||
        return faults.Fault(exc.HTTPNotImplemented())
 | 
					        return faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        'list_collections': {
 | 
				
			||||||
 | 
					            'public':  {'item_name': 'ip', 'item_key': 'addr'},
 | 
				
			||||||
 | 
					            'private': {'item_name': 'ip', 'item_key': 'addr'},
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(metadata=metadata,
 | 
				
			||||||
 | 
					                                              xmlns=wsgi.XMLNS_V10),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller(), serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,10 +30,11 @@ from collections import defaultdict
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from webob.dec import wsgify
 | 
					from webob.dec import wsgify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from nova import wsgi
 | 
					from nova import wsgi as base_wsgi
 | 
				
			||||||
from nova.api.openstack import common
 | 
					from nova.api.openstack import common
 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
from nova.api.openstack.views import limits as limits_views
 | 
					from nova.api.openstack.views import limits as limits_views
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Convenience constants for the limits dictionary passed to Limiter().
 | 
					# Convenience constants for the limits dictionary passed to Limiter().
 | 
				
			||||||
@@ -43,23 +44,11 @@ PER_HOUR = 60 * 60
 | 
				
			|||||||
PER_DAY = 60 * 60 * 24
 | 
					PER_DAY = 60 * 60 * 24
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LimitsController(common.OpenstackController):
 | 
					class LimitsController(object):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Controller for accessing limits in the OpenStack API.
 | 
					    Controller for accessing limits in the OpenStack API.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        "application/xml": {
 | 
					 | 
				
			||||||
            "attributes": {
 | 
					 | 
				
			||||||
                "limit": ["verb", "URI", "uri", "regex", "value", "unit",
 | 
					 | 
				
			||||||
                    "resetTime", "next-available", "remaining", "name"],
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "plurals": {
 | 
					 | 
				
			||||||
                "rate": "limit",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def index(self, req):
 | 
					    def index(self, req):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Return all global and rate limit information.
 | 
					        Return all global and rate limit information.
 | 
				
			||||||
@@ -84,6 +73,35 @@ class LimitsControllerV11(LimitsController):
 | 
				
			|||||||
        return limits_views.ViewBuilderV11()
 | 
					        return limits_views.ViewBuilderV11()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory(version='1.0'):
 | 
				
			||||||
 | 
					    controller = {
 | 
				
			||||||
 | 
					        '1.0': LimitsControllerV10,
 | 
				
			||||||
 | 
					        '1.1': LimitsControllerV11,
 | 
				
			||||||
 | 
					    }[version]()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    xmlns = {
 | 
				
			||||||
 | 
					        '1.0': wsgi.XMLNS_V10,
 | 
				
			||||||
 | 
					        '1.1': wsgi.XMLNS_V11,
 | 
				
			||||||
 | 
					    }[version]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        "attributes": {
 | 
				
			||||||
 | 
					            "limit": ["verb", "URI", "uri", "regex", "value", "unit",
 | 
				
			||||||
 | 
					                "resetTime", "next-available", "remaining", "name"],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "plurals": {
 | 
				
			||||||
 | 
					            "rate": "limit",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(xmlns=xmlns,
 | 
				
			||||||
 | 
					                                              metadata=metadata)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(controller, serializers=serializers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Limit(object):
 | 
					class Limit(object):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Stores information about a limit for HTTP requets.
 | 
					    Stores information about a limit for HTTP requets.
 | 
				
			||||||
@@ -195,7 +213,7 @@ DEFAULT_LIMITS = [
 | 
				
			|||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RateLimitingMiddleware(wsgi.Middleware):
 | 
					class RateLimitingMiddleware(base_wsgi.Middleware):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Rate-limits requests passing through this middleware. All limit information
 | 
					    Rate-limits requests passing through this middleware. All limit information
 | 
				
			||||||
    is stored in memory for this implementation.
 | 
					    is stored in memory for this implementation.
 | 
				
			||||||
@@ -209,7 +227,7 @@ class RateLimitingMiddleware(wsgi.Middleware):
 | 
				
			|||||||
        @param application: WSGI application to wrap
 | 
					        @param application: WSGI application to wrap
 | 
				
			||||||
        @param limits: List of dictionaries describing limits
 | 
					        @param limits: List of dictionaries describing limits
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        wsgi.Middleware.__init__(self, application)
 | 
					        base_wsgi.Middleware.__init__(self, application)
 | 
				
			||||||
        self._limiter = Limiter(limits or DEFAULT_LIMITS)
 | 
					        self._limiter = Limiter(limits or DEFAULT_LIMITS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @wsgify(RequestClass=wsgi.Request)
 | 
					    @wsgify(RequestClass=wsgi.Request)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,12 +19,11 @@ from webob import exc
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from nova import compute
 | 
					from nova import compute
 | 
				
			||||||
from nova import quota
 | 
					from nova import quota
 | 
				
			||||||
from nova import wsgi
 | 
					 | 
				
			||||||
from nova.api.openstack import common
 | 
					 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
    """ The server metadata API controller for the Openstack API """
 | 
					    """ The server metadata API controller for the Openstack API """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
@@ -43,10 +42,9 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        return self._get_metadata(context, server_id)
 | 
					        return self._get_metadata(context, server_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req, server_id):
 | 
					    def create(self, req, server_id, body):
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        data = self._deserialize(req.body, req.get_content_type())
 | 
					        metadata = body.get('metadata')
 | 
				
			||||||
        metadata = data.get('metadata')
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.compute_api.update_or_create_instance_metadata(context,
 | 
					            self.compute_api.update_or_create_instance_metadata(context,
 | 
				
			||||||
                                                                server_id,
 | 
					                                                                server_id,
 | 
				
			||||||
@@ -55,9 +53,8 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
            self._handle_quota_error(error)
 | 
					            self._handle_quota_error(error)
 | 
				
			||||||
        return req.body
 | 
					        return req.body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, req, server_id, id):
 | 
					    def update(self, req, server_id, id, body):
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        body = self._deserialize(req.body, req.get_content_type())
 | 
					 | 
				
			||||||
        if not id in body:
 | 
					        if not id in body:
 | 
				
			||||||
            expl = _('Request body and URI mismatch')
 | 
					            expl = _('Request body and URI mismatch')
 | 
				
			||||||
            raise exc.HTTPBadRequest(explanation=expl)
 | 
					            raise exc.HTTPBadRequest(explanation=expl)
 | 
				
			||||||
@@ -92,3 +89,11 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        if error.code == "MetadataLimitExceeded":
 | 
					        if error.code == "MetadataLimitExceeded":
 | 
				
			||||||
            raise exc.HTTPBadRequest(explanation=error.message)
 | 
					            raise exc.HTTPBadRequest(explanation=error.message)
 | 
				
			||||||
        raise error
 | 
					        raise error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(xmlns=wsgi.XMLNS_V11),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller(), serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ import nova.api.openstack.views.addresses
 | 
				
			|||||||
import nova.api.openstack.views.flavors
 | 
					import nova.api.openstack.views.flavors
 | 
				
			||||||
import nova.api.openstack.views.images
 | 
					import nova.api.openstack.views.images
 | 
				
			||||||
import nova.api.openstack.views.servers
 | 
					import nova.api.openstack.views.servers
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
from nova.auth import manager as auth_manager
 | 
					from nova.auth import manager as auth_manager
 | 
				
			||||||
from nova.compute import instance_types
 | 
					from nova.compute import instance_types
 | 
				
			||||||
import nova.api.openstack
 | 
					import nova.api.openstack
 | 
				
			||||||
@@ -41,31 +42,12 @@ LOG = logging.getLogger('nova.api.openstack.servers')
 | 
				
			|||||||
FLAGS = flags.FLAGS
 | 
					FLAGS = flags.FLAGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
    """ The Server API controller for the OpenStack API """
 | 
					    """ The Server API controller for the OpenStack API """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        "application/xml": {
 | 
					 | 
				
			||||||
            "attributes": {
 | 
					 | 
				
			||||||
                "server": ["id", "imageId", "name", "flavorId", "hostId",
 | 
					 | 
				
			||||||
                           "status", "progress", "adminPass", "flavorRef",
 | 
					 | 
				
			||||||
                           "imageRef"],
 | 
					 | 
				
			||||||
                "link": ["rel", "type", "href"],
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "dict_collections": {
 | 
					 | 
				
			||||||
                "metadata": {"item_name": "meta", "item_key": "key"},
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "list_collections": {
 | 
					 | 
				
			||||||
                "public": {"item_name": "ip", "item_key": "addr"},
 | 
					 | 
				
			||||||
                "private": {"item_name": "ip", "item_key": "addr"},
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.compute_api = compute.API()
 | 
					        self.compute_api = compute.API()
 | 
				
			||||||
        self._image_service = utils.import_object(FLAGS.image_service)
 | 
					        self._image_service = utils.import_object(FLAGS.image_service)
 | 
				
			||||||
        super(Controller, self).__init__()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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 """
 | 
				
			||||||
@@ -122,15 +104,14 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
            return faults.Fault(exc.HTTPNotFound())
 | 
					            return faults.Fault(exc.HTTPNotFound())
 | 
				
			||||||
        return exc.HTTPAccepted()
 | 
					        return exc.HTTPAccepted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req):
 | 
					    def create(self, req, body):
 | 
				
			||||||
        """ Creates a new server for a given user """
 | 
					        """ Creates a new server for a given user """
 | 
				
			||||||
        env = self._deserialize_create(req)
 | 
					        if not body:
 | 
				
			||||||
        if not env:
 | 
					 | 
				
			||||||
            return faults.Fault(exc.HTTPUnprocessableEntity())
 | 
					            return faults.Fault(exc.HTTPUnprocessableEntity())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        password = self._get_server_admin_password(env['server'])
 | 
					        password = self._get_server_admin_password(body['server'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        key_name = None
 | 
					        key_name = None
 | 
				
			||||||
        key_data = None
 | 
					        key_data = None
 | 
				
			||||||
@@ -140,7 +121,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
            key_name = key_pair['name']
 | 
					            key_name = key_pair['name']
 | 
				
			||||||
            key_data = key_pair['public_key']
 | 
					            key_data = key_pair['public_key']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        requested_image_id = self._image_id_from_req_data(env)
 | 
					        requested_image_id = self._image_id_from_req_data(body)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            image_id = common.get_image_id_from_image_hash(self._image_service,
 | 
					            image_id = common.get_image_id_from_image_hash(self._image_service,
 | 
				
			||||||
                context, requested_image_id)
 | 
					                context, requested_image_id)
 | 
				
			||||||
@@ -151,18 +132,18 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image(
 | 
					        kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image(
 | 
				
			||||||
            req, image_id)
 | 
					            req, image_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        personality = env['server'].get('personality')
 | 
					        personality = body['server'].get('personality')
 | 
				
			||||||
        injected_files = []
 | 
					        injected_files = []
 | 
				
			||||||
        if personality:
 | 
					        if personality:
 | 
				
			||||||
            injected_files = self._get_injected_files(personality)
 | 
					            injected_files = self._get_injected_files(personality)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        flavor_id = self._flavor_id_from_req_data(env)
 | 
					        flavor_id = self._flavor_id_from_req_data(body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not 'name' in env['server']:
 | 
					        if not 'name' in body['server']:
 | 
				
			||||||
            msg = _("Server name is not defined")
 | 
					            msg = _("Server name is not defined")
 | 
				
			||||||
            return exc.HTTPBadRequest(msg)
 | 
					            return exc.HTTPBadRequest(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        name = env['server']['name']
 | 
					        name = body['server']['name']
 | 
				
			||||||
        self._validate_server_name(name)
 | 
					        self._validate_server_name(name)
 | 
				
			||||||
        name = name.strip()
 | 
					        name = name.strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -179,7 +160,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
                display_description=name,
 | 
					                display_description=name,
 | 
				
			||||||
                key_name=key_name,
 | 
					                key_name=key_name,
 | 
				
			||||||
                key_data=key_data,
 | 
					                key_data=key_data,
 | 
				
			||||||
                metadata=env['server'].get('metadata', {}),
 | 
					                metadata=body['server'].get('metadata', {}),
 | 
				
			||||||
                injected_files=injected_files)
 | 
					                injected_files=injected_files)
 | 
				
			||||||
        except quota.QuotaError as error:
 | 
					        except quota.QuotaError as error:
 | 
				
			||||||
            self._handle_quota_error(error)
 | 
					            self._handle_quota_error(error)
 | 
				
			||||||
@@ -194,18 +175,6 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
                                            password)
 | 
					                                            password)
 | 
				
			||||||
        return server
 | 
					        return server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _deserialize_create(self, request):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Deserialize a create request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Overrides normal behavior in the case of xml content
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if request.content_type == "application/xml":
 | 
					 | 
				
			||||||
            deserializer = ServerCreateRequestXMLDeserializer()
 | 
					 | 
				
			||||||
            return deserializer.deserialize(request.body)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            return self._deserialize(request.body, request.get_content_type())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _get_injected_files(self, personality):
 | 
					    def _get_injected_files(self, personality):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Create a list of injected files from the personality attribute
 | 
					        Create a list of injected files from the personality attribute
 | 
				
			||||||
@@ -255,24 +224,23 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        return utils.generate_password(16)
 | 
					        return utils.generate_password(16)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @scheduler_api.redirect_handler
 | 
					    @scheduler_api.redirect_handler
 | 
				
			||||||
    def update(self, req, id):
 | 
					    def update(self, req, id, body):
 | 
				
			||||||
        """ Updates the server name or password """
 | 
					        """ Updates the server name or password """
 | 
				
			||||||
        if len(req.body) == 0:
 | 
					        if len(req.body) == 0:
 | 
				
			||||||
            raise exc.HTTPUnprocessableEntity()
 | 
					            raise exc.HTTPUnprocessableEntity()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        inst_dict = self._deserialize(req.body, req.get_content_type())
 | 
					        if not body:
 | 
				
			||||||
        if not inst_dict:
 | 
					 | 
				
			||||||
            return faults.Fault(exc.HTTPUnprocessableEntity())
 | 
					            return faults.Fault(exc.HTTPUnprocessableEntity())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ctxt = req.environ['nova.context']
 | 
					        ctxt = req.environ['nova.context']
 | 
				
			||||||
        update_dict = {}
 | 
					        update_dict = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if 'name' in inst_dict['server']:
 | 
					        if 'name' in body['server']:
 | 
				
			||||||
            name = inst_dict['server']['name']
 | 
					            name = body['server']['name']
 | 
				
			||||||
            self._validate_server_name(name)
 | 
					            self._validate_server_name(name)
 | 
				
			||||||
            update_dict['display_name'] = name.strip()
 | 
					            update_dict['display_name'] = name.strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._parse_update(ctxt, id, inst_dict, update_dict)
 | 
					        self._parse_update(ctxt, id, body, update_dict)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.compute_api.update(ctxt, id, **update_dict)
 | 
					            self.compute_api.update(ctxt, id, **update_dict)
 | 
				
			||||||
@@ -294,7 +262,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @scheduler_api.redirect_handler
 | 
					    @scheduler_api.redirect_handler
 | 
				
			||||||
    def action(self, req, id):
 | 
					    def action(self, req, id, body):
 | 
				
			||||||
        """Multi-purpose method used to reboot, rebuild, or
 | 
					        """Multi-purpose method used to reboot, rebuild, or
 | 
				
			||||||
        resize a server"""
 | 
					        resize a server"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -307,10 +275,9 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
            'rebuild': self._action_rebuild,
 | 
					            'rebuild': self._action_rebuild,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        input_dict = self._deserialize(req.body, req.get_content_type())
 | 
					 | 
				
			||||||
        for key in actions.keys():
 | 
					        for key in actions.keys():
 | 
				
			||||||
            if key in input_dict:
 | 
					            if key in body:
 | 
				
			||||||
                return actions[key](input_dict, req, id)
 | 
					                return actions[key](body, req, id)
 | 
				
			||||||
        return faults.Fault(exc.HTTPNotImplemented())
 | 
					        return faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _action_change_password(self, input_dict, req, id):
 | 
					    def _action_change_password(self, input_dict, req, id):
 | 
				
			||||||
@@ -410,7 +377,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        return exc.HTTPAccepted()
 | 
					        return exc.HTTPAccepted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @scheduler_api.redirect_handler
 | 
					    @scheduler_api.redirect_handler
 | 
				
			||||||
    def reset_network(self, req, id):
 | 
					    def reset_network(self, req, id, body):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Reset networking on an instance (admin only).
 | 
					        Reset networking on an instance (admin only).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -425,7 +392,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        return exc.HTTPAccepted()
 | 
					        return exc.HTTPAccepted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @scheduler_api.redirect_handler
 | 
					    @scheduler_api.redirect_handler
 | 
				
			||||||
    def inject_network_info(self, req, id):
 | 
					    def inject_network_info(self, req, id, body):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Inject network info for an instance (admin only).
 | 
					        Inject network info for an instance (admin only).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -440,7 +407,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        return exc.HTTPAccepted()
 | 
					        return exc.HTTPAccepted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @scheduler_api.redirect_handler
 | 
					    @scheduler_api.redirect_handler
 | 
				
			||||||
    def pause(self, req, id):
 | 
					    def pause(self, req, id, body):
 | 
				
			||||||
        """ Permit Admins to Pause the server. """
 | 
					        """ Permit Admins to Pause the server. """
 | 
				
			||||||
        ctxt = req.environ['nova.context']
 | 
					        ctxt = req.environ['nova.context']
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@@ -452,7 +419,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        return exc.HTTPAccepted()
 | 
					        return exc.HTTPAccepted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @scheduler_api.redirect_handler
 | 
					    @scheduler_api.redirect_handler
 | 
				
			||||||
    def unpause(self, req, id):
 | 
					    def unpause(self, req, id, body):
 | 
				
			||||||
        """ Permit Admins to Unpause the server. """
 | 
					        """ Permit Admins to Unpause the server. """
 | 
				
			||||||
        ctxt = req.environ['nova.context']
 | 
					        ctxt = req.environ['nova.context']
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@@ -464,7 +431,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        return exc.HTTPAccepted()
 | 
					        return exc.HTTPAccepted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @scheduler_api.redirect_handler
 | 
					    @scheduler_api.redirect_handler
 | 
				
			||||||
    def suspend(self, req, id):
 | 
					    def suspend(self, req, id, body):
 | 
				
			||||||
        """permit admins to suspend the server"""
 | 
					        """permit admins to suspend the server"""
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@@ -476,7 +443,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        return exc.HTTPAccepted()
 | 
					        return exc.HTTPAccepted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @scheduler_api.redirect_handler
 | 
					    @scheduler_api.redirect_handler
 | 
				
			||||||
    def resume(self, req, id):
 | 
					    def resume(self, req, id, body):
 | 
				
			||||||
        """permit admins to resume the server from suspend"""
 | 
					        """permit admins to resume the server from suspend"""
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@@ -815,3 +782,44 @@ class ServerCreateRequestXMLDeserializer(object):
 | 
				
			|||||||
            if child.nodeType == child.TEXT_NODE:
 | 
					            if child.nodeType == child.TEXT_NODE:
 | 
				
			||||||
                return child.nodeValue
 | 
					                return child.nodeValue
 | 
				
			||||||
        return ""
 | 
					        return ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory(version='1.0'):
 | 
				
			||||||
 | 
					    controller = {
 | 
				
			||||||
 | 
					        '1.0': ControllerV10,
 | 
				
			||||||
 | 
					        '1.1': ControllerV11,
 | 
				
			||||||
 | 
					    }[version]()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        "attributes": {
 | 
				
			||||||
 | 
					            "server": ["id", "imageId", "name", "flavorId", "hostId",
 | 
				
			||||||
 | 
					                       "status", "progress", "adminPass", "flavorRef",
 | 
				
			||||||
 | 
					                       "imageRef"],
 | 
				
			||||||
 | 
					            "link": ["rel", "type", "href"],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "dict_collections": {
 | 
				
			||||||
 | 
					            "metadata": {"item_name": "meta", "item_key": "key"},
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "list_collections": {
 | 
				
			||||||
 | 
					            "public": {"item_name": "ip", "item_key": "addr"},
 | 
				
			||||||
 | 
					            "private": {"item_name": "ip", "item_key": "addr"},
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    xmlns = {
 | 
				
			||||||
 | 
					        '1.0': wsgi.XMLNS_V10,
 | 
				
			||||||
 | 
					        '1.1': wsgi.XMLNS_V11,
 | 
				
			||||||
 | 
					    }[version]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(metadata=metadata,
 | 
				
			||||||
 | 
					                                              xmlns=xmlns),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    deserializers = {
 | 
				
			||||||
 | 
					        'application/xml': ServerCreateRequestXMLDeserializer(),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(controller, serializers=serializers,
 | 
				
			||||||
 | 
					                         deserializers=deserializers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,29 +17,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from webob import exc
 | 
					from webob import exc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from nova.api.openstack import common
 | 
					 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _translate_keys(inst):
 | 
					class Controller(object):
 | 
				
			||||||
    """ Coerces a shared IP group instance into proper dictionary format """
 | 
					 | 
				
			||||||
    return dict(sharedIpGroup=inst)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _translate_detail_keys(inst):
 | 
					 | 
				
			||||||
    """ Coerces a shared IP group instance into proper dictionary format with
 | 
					 | 
				
			||||||
    correctly mapped attributes """
 | 
					 | 
				
			||||||
    return dict(sharedIpGroups=inst)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					 | 
				
			||||||
    """ The Shared IP Groups Controller for the Openstack API """
 | 
					    """ The Shared IP Groups Controller for the Openstack API """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            'attributes': {
 | 
					 | 
				
			||||||
                'sharedIpGroup': []}}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def index(self, req):
 | 
					    def index(self, req):
 | 
				
			||||||
        """ Returns a list of Shared IP Groups for the user """
 | 
					        """ Returns a list of Shared IP Groups for the user """
 | 
				
			||||||
        raise faults.Fault(exc.HTTPNotImplemented())
 | 
					        raise faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
@@ -48,7 +32,7 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        """ Shows in-depth information on a specific Shared IP Group """
 | 
					        """ Shows in-depth information on a specific Shared IP Group """
 | 
				
			||||||
        raise faults.Fault(exc.HTTPNotImplemented())
 | 
					        raise faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, req, id):
 | 
					    def update(self, req, id, body):
 | 
				
			||||||
        """ You can't update a Shared IP Group """
 | 
					        """ You can't update a Shared IP Group """
 | 
				
			||||||
        raise faults.Fault(exc.HTTPNotImplemented())
 | 
					        raise faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -60,6 +44,10 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        """ Returns a complete list of Shared IP Groups """
 | 
					        """ Returns a complete list of Shared IP Groups """
 | 
				
			||||||
        raise faults.Fault(exc.HTTPNotImplemented())
 | 
					        raise faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req):
 | 
					    def create(self, req, body):
 | 
				
			||||||
        """ Creates a new Shared IP group """
 | 
					        """ Creates a new Shared IP group """
 | 
				
			||||||
        raise faults.Fault(exc.HTTPNotImplemented())
 | 
					        raise faults.Fault(exc.HTTPNotImplemented())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,8 +20,10 @@ from nova import flags
 | 
				
			|||||||
from nova import log as logging
 | 
					from nova import log as logging
 | 
				
			||||||
from nova.api.openstack import common
 | 
					from nova.api.openstack import common
 | 
				
			||||||
from nova.api.openstack import faults
 | 
					from nova.api.openstack import faults
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
from nova.auth import manager
 | 
					from nova.auth import manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FLAGS = flags.FLAGS
 | 
					FLAGS = flags.FLAGS
 | 
				
			||||||
LOG = logging.getLogger('nova.api.openstack')
 | 
					LOG = logging.getLogger('nova.api.openstack')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,12 +36,7 @@ def _translate_keys(user):
 | 
				
			|||||||
                admin=user.admin)
 | 
					                admin=user.admin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            "attributes": {
 | 
					 | 
				
			||||||
                "user": ["id", "name", "access", "secret", "admin"]}}}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.manager = manager.AuthManager()
 | 
					        self.manager = manager.AuthManager()
 | 
				
			||||||
@@ -81,23 +78,35 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        self.manager.delete_user(id)
 | 
					        self.manager.delete_user(id)
 | 
				
			||||||
        return {}
 | 
					        return {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req):
 | 
					    def create(self, req, body):
 | 
				
			||||||
        self._check_admin(req.environ['nova.context'])
 | 
					        self._check_admin(req.environ['nova.context'])
 | 
				
			||||||
        env = self._deserialize(req.body, req.get_content_type())
 | 
					        is_admin = body['user'].get('admin') in ('T', 'True', True)
 | 
				
			||||||
        is_admin = env['user'].get('admin') in ('T', 'True', True)
 | 
					        name = body['user'].get('name')
 | 
				
			||||||
        name = env['user'].get('name')
 | 
					        access = body['user'].get('access')
 | 
				
			||||||
        access = env['user'].get('access')
 | 
					        secret = body['user'].get('secret')
 | 
				
			||||||
        secret = env['user'].get('secret')
 | 
					 | 
				
			||||||
        user = self.manager.create_user(name, access, secret, is_admin)
 | 
					        user = self.manager.create_user(name, access, secret, is_admin)
 | 
				
			||||||
        return dict(user=_translate_keys(user))
 | 
					        return dict(user=_translate_keys(user))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, req, id):
 | 
					    def update(self, req, id, body):
 | 
				
			||||||
        self._check_admin(req.environ['nova.context'])
 | 
					        self._check_admin(req.environ['nova.context'])
 | 
				
			||||||
        env = self._deserialize(req.body, req.get_content_type())
 | 
					        is_admin = body['user'].get('admin')
 | 
				
			||||||
        is_admin = env['user'].get('admin')
 | 
					 | 
				
			||||||
        if is_admin is not None:
 | 
					        if is_admin is not None:
 | 
				
			||||||
            is_admin = is_admin in ('T', 'True', True)
 | 
					            is_admin = is_admin in ('T', 'True', True)
 | 
				
			||||||
        access = env['user'].get('access')
 | 
					        access = body['user'].get('access')
 | 
				
			||||||
        secret = env['user'].get('secret')
 | 
					        secret = body['user'].get('secret')
 | 
				
			||||||
        self.manager.modify_user(id, access, secret, is_admin)
 | 
					        self.manager.modify_user(id, access, secret, is_admin)
 | 
				
			||||||
        return dict(user=_translate_keys(self.manager.get_user(id)))
 | 
					        return dict(user=_translate_keys(self.manager.get_user(id)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        "attributes": {
 | 
				
			||||||
 | 
					            "user": ["id", "name", "access", "secret", "admin"],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(metadata=metadata),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller(), serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										291
									
								
								nova/api/openstack/wsgi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								nova/api/openstack/wsgi.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,291 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import webob
 | 
				
			||||||
 | 
					from xml.dom import minidom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from nova import exception
 | 
				
			||||||
 | 
					from nova import log as logging
 | 
				
			||||||
 | 
					from nova import utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
 | 
				
			||||||
 | 
					XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = logging.getLogger('nova.api.openstack.wsgi')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Request(webob.Request):
 | 
				
			||||||
 | 
					    def best_match_content_type(self, supported=None):
 | 
				
			||||||
 | 
					        """Determine the requested content-type.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Based on the query extension then the Accept header.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param supported: list of content-types to override defaults
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        supported = supported or ['application/json', 'application/xml']
 | 
				
			||||||
 | 
					        parts = self.path.rsplit('.', 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if len(parts) > 1:
 | 
				
			||||||
 | 
					            ctype = 'application/{0}'.format(parts[1])
 | 
				
			||||||
 | 
					            if ctype in supported:
 | 
				
			||||||
 | 
					                return ctype
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bm = self.accept.best_match(supported)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return bm or 'application/json'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_content_type(self):
 | 
				
			||||||
 | 
					        if not "Content-Type" in self.headers:
 | 
				
			||||||
 | 
					            raise exception.InvalidContentType(content_type=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        allowed_types = ("application/xml", "application/json")
 | 
				
			||||||
 | 
					        type = self.content_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if type not in allowed_types:
 | 
				
			||||||
 | 
					            raise exception.InvalidContentType(content_type=type)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONDeserializer(object):
 | 
				
			||||||
 | 
					    def deserialize(self, datastring):
 | 
				
			||||||
 | 
					        return utils.loads(datastring)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONSerializer(object):
 | 
				
			||||||
 | 
					    def serialize(self, data):
 | 
				
			||||||
 | 
					        return utils.dumps(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class XMLDeserializer(object):
 | 
				
			||||||
 | 
					    def __init__(self, metadata=None):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        :param metadata: information needed to deserialize xml into
 | 
				
			||||||
 | 
					                         a dictionary.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        super(XMLDeserializer, self).__init__()
 | 
				
			||||||
 | 
					        self.metadata = metadata or {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def deserialize(self, datastring):
 | 
				
			||||||
 | 
					        """XML deserialization entry point."""
 | 
				
			||||||
 | 
					        plurals = set(self.metadata.get('plurals', {}))
 | 
				
			||||||
 | 
					        node = minidom.parseString(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():
 | 
				
			||||||
 | 
					                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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class XMLSerializer(object):
 | 
				
			||||||
 | 
					    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(XMLSerializer, self).__init__()
 | 
				
			||||||
 | 
					        self.metadata = metadata or {}
 | 
				
			||||||
 | 
					        self.xmlns = xmlns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def serialize(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])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        xmlns = node.getAttribute('xmlns')
 | 
				
			||||||
 | 
					        if not xmlns and self.xmlns:
 | 
				
			||||||
 | 
					            node.setAttribute('xmlns', self.xmlns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return node.toprettyxml(indent='    ')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _to_xml_node(self, doc, metadata, nodename, data):
 | 
				
			||||||
 | 
					        """Recursive method to convert data members to XML nodes."""
 | 
				
			||||||
 | 
					        result = doc.createElement(nodename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Set the xml namespace if one is specified
 | 
				
			||||||
 | 
					        # TODO(justinsb): We could also use prefixes on the keys
 | 
				
			||||||
 | 
					        xmlns = metadata.get('xmlns', None)
 | 
				
			||||||
 | 
					        if xmlns:
 | 
				
			||||||
 | 
					            result.setAttribute('xmlns', xmlns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if type(data) is list:
 | 
				
			||||||
 | 
					            collections = metadata.get('list_collections', {})
 | 
				
			||||||
 | 
					            if nodename in collections:
 | 
				
			||||||
 | 
					                metadata = collections[nodename]
 | 
				
			||||||
 | 
					                for item in data:
 | 
				
			||||||
 | 
					                    node = doc.createElement(metadata['item_name'])
 | 
				
			||||||
 | 
					                    node.setAttribute(metadata['item_key'], str(item))
 | 
				
			||||||
 | 
					                    result.appendChild(node)
 | 
				
			||||||
 | 
					                return result
 | 
				
			||||||
 | 
					            singular = metadata.get('plurals', {}).get(nodename, None)
 | 
				
			||||||
 | 
					            if singular is None:
 | 
				
			||||||
 | 
					                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)
 | 
				
			||||||
 | 
					        elif type(data) is dict:
 | 
				
			||||||
 | 
					            collections = metadata.get('dict_collections', {})
 | 
				
			||||||
 | 
					            if nodename in collections:
 | 
				
			||||||
 | 
					                metadata = collections[nodename]
 | 
				
			||||||
 | 
					                for k, v in data.items():
 | 
				
			||||||
 | 
					                    node = doc.createElement(metadata['item_name'])
 | 
				
			||||||
 | 
					                    node.setAttribute(metadata['item_key'], str(k))
 | 
				
			||||||
 | 
					                    text = doc.createTextNode(str(v))
 | 
				
			||||||
 | 
					                    node.appendChild(text)
 | 
				
			||||||
 | 
					                    result.appendChild(node)
 | 
				
			||||||
 | 
					                return result
 | 
				
			||||||
 | 
					            attrs = metadata.get('attributes', {}).get(nodename, {})
 | 
				
			||||||
 | 
					            for k, v in data.items():
 | 
				
			||||||
 | 
					                if k in attrs:
 | 
				
			||||||
 | 
					                    result.setAttribute(k, str(v))
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    node = self._to_xml_node(doc, metadata, k, v)
 | 
				
			||||||
 | 
					                    result.appendChild(node)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # Type is atom
 | 
				
			||||||
 | 
					            node = doc.createTextNode(str(data))
 | 
				
			||||||
 | 
					            result.appendChild(node)
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Resource(object):
 | 
				
			||||||
 | 
					    """WSGI app that dispatched to methods.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    WSGI app that reads routing information supplied by RoutesMiddleware
 | 
				
			||||||
 | 
					    and calls the requested action method upon itself.  All action methods
 | 
				
			||||||
 | 
					    must, in addition to their normal parameters, accept a 'req' argument
 | 
				
			||||||
 | 
					    which is the incoming wsgi.Request.  They raise a webob.exc exception,
 | 
				
			||||||
 | 
					    or return a dict which will be serialized by requested content type.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self, controller, serializers=None, deserializers=None):
 | 
				
			||||||
 | 
					        self.serializers = {
 | 
				
			||||||
 | 
					            'application/xml': XMLSerializer(),
 | 
				
			||||||
 | 
					            'application/json': JSONSerializer(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.serializers.update(serializers or {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.deserializers = {
 | 
				
			||||||
 | 
					            'application/xml': XMLDeserializer(),
 | 
				
			||||||
 | 
					            'application/json': JSONDeserializer(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.deserializers.update(deserializers or {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.controller = controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @webob.dec.wsgify(RequestClass=Request)
 | 
				
			||||||
 | 
					    def __call__(self, request):
 | 
				
			||||||
 | 
					        """Call the method specified in req.environ by RoutesMiddleware."""
 | 
				
			||||||
 | 
					        LOG.debug("%s %s" % (request.method, request.url))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            action, action_args, accept = self.deserialize_request(request)
 | 
				
			||||||
 | 
					        except exception.InvalidContentType:
 | 
				
			||||||
 | 
					            return webob.exc.HTTPBadRequest(_("Unsupported Content-Type"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        controller_method = getattr(self.controller, action)
 | 
				
			||||||
 | 
					        result = controller_method(req=request, **action_args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.serialize_response(accept, result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            msg_dict = dict(url=request.url, status=response.status_int)
 | 
				
			||||||
 | 
					            msg = _("%(url)s returned with HTTP %(status)d") % msg_dict
 | 
				
			||||||
 | 
					        except AttributeError:
 | 
				
			||||||
 | 
					            msg_dict = dict(url=request.url)
 | 
				
			||||||
 | 
					            msg = _("%(url)s returned a fault")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.debug(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def serialize_response(self, content_type, response_body):
 | 
				
			||||||
 | 
					        """Serialize a dict into a string and wrap in a wsgi.Request object.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param content_type: expected mimetype of serialized response body
 | 
				
			||||||
 | 
					        :param response_body: dict produced by the Controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if not type(response_body) is dict:
 | 
				
			||||||
 | 
					            return response_body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = webob.Response()
 | 
				
			||||||
 | 
					        response.headers['Content-Type'] = content_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        serializer = self.get_serializer(content_type)
 | 
				
			||||||
 | 
					        response.body = serializer.serialize(response_body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_serializer(self, content_type):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return self.serializers[content_type]
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            raise exception.InvalidContentType(content_type=content_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def deserialize_request(self, request):
 | 
				
			||||||
 | 
					        """Parse a wsgi request into a set of params we care about.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param request: wsgi.Request object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        action_args = self.get_action_args(request.environ)
 | 
				
			||||||
 | 
					        action = action_args.pop('action')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if request.method.lower() in ('post', 'put'):
 | 
				
			||||||
 | 
					            if len(request.body) == 0:
 | 
				
			||||||
 | 
					                action_args['body'] = None
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                content_type = request.get_content_type()
 | 
				
			||||||
 | 
					                deserializer = self.get_deserializer(content_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    action_args['body'] = deserializer.deserialize(request.body)
 | 
				
			||||||
 | 
					                except exception.InvalidContentType:
 | 
				
			||||||
 | 
					                    action_args['body'] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        accept = self.get_expected_content_type(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (action, action_args, accept)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_expected_content_type(self, request):
 | 
				
			||||||
 | 
					        return request.best_match_content_type()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_action_args(self, request_environment):
 | 
				
			||||||
 | 
					        args = request_environment['wsgiorg.routing_args'][1].copy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        del args['controller']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'format' in args:
 | 
				
			||||||
 | 
					            del args['format']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_deserializer(self, content_type):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return self.deserializers[content_type]
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            raise exception.InvalidContentType(content_type=content_type)
 | 
				
			||||||
@@ -17,6 +17,7 @@ from nova import db
 | 
				
			|||||||
from nova import flags
 | 
					from nova import flags
 | 
				
			||||||
from nova import log as logging
 | 
					from nova import log as logging
 | 
				
			||||||
from nova.api.openstack import common
 | 
					from nova.api.openstack import common
 | 
				
			||||||
 | 
					from nova.api.openstack import wsgi
 | 
				
			||||||
from nova.scheduler import api
 | 
					from nova.scheduler import api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,12 +42,7 @@ def _scrub_zone(zone):
 | 
				
			|||||||
                    'deleted', 'deleted_at', 'updated_at'))
 | 
					                    'deleted', 'deleted_at', 'updated_at'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Controller(common.OpenstackController):
 | 
					class Controller(object):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    _serialization_metadata = {
 | 
					 | 
				
			||||||
        'application/xml': {
 | 
					 | 
				
			||||||
            "attributes": {
 | 
					 | 
				
			||||||
                "zone": ["id", "api_url", "name", "capabilities"]}}}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def index(self, req):
 | 
					    def index(self, req):
 | 
				
			||||||
        """Return all zones in brief"""
 | 
					        """Return all zones in brief"""
 | 
				
			||||||
@@ -85,15 +81,28 @@ class Controller(common.OpenstackController):
 | 
				
			|||||||
        api.zone_delete(req.environ['nova.context'], zone_id)
 | 
					        api.zone_delete(req.environ['nova.context'], zone_id)
 | 
				
			||||||
        return {}
 | 
					        return {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, req):
 | 
					    def create(self, req, body):
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        env = self._deserialize(req.body, req.get_content_type())
 | 
					        zone = api.zone_create(context, body["zone"])
 | 
				
			||||||
        zone = api.zone_create(context, env["zone"])
 | 
					 | 
				
			||||||
        return dict(zone=_scrub_zone(zone))
 | 
					        return dict(zone=_scrub_zone(zone))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, req, id):
 | 
					    def update(self, req, id, body):
 | 
				
			||||||
        context = req.environ['nova.context']
 | 
					        context = req.environ['nova.context']
 | 
				
			||||||
        env = self._deserialize(req.body, req.get_content_type())
 | 
					 | 
				
			||||||
        zone_id = int(id)
 | 
					        zone_id = int(id)
 | 
				
			||||||
        zone = api.zone_update(context, zone_id, env["zone"])
 | 
					        zone = api.zone_update(context, zone_id, body["zone"])
 | 
				
			||||||
        return dict(zone=_scrub_zone(zone))
 | 
					        return dict(zone=_scrub_zone(zone))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resource_factory():
 | 
				
			||||||
 | 
					    metadata = {
 | 
				
			||||||
 | 
					        "attributes": {
 | 
				
			||||||
 | 
					            "zone": ["id", "api_url", "name", "capabilities"],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializers = {
 | 
				
			||||||
 | 
					        'application/xml': wsgi.XMLSerializer(xmlns=wsgi.XMLNS_V10,
 | 
				
			||||||
 | 
					                                              metadata=metadata),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return wsgi.Resource(Controller(), serializers=serializers)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,7 +65,7 @@ class LimitsControllerV10Test(BaseLimitTestSuite):
 | 
				
			|||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        """Run before each test."""
 | 
					        """Run before each test."""
 | 
				
			||||||
        BaseLimitTestSuite.setUp(self)
 | 
					        BaseLimitTestSuite.setUp(self)
 | 
				
			||||||
        self.controller = limits.LimitsControllerV10()
 | 
					        self.controller = limits.resource_factory('1.0')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_index_request(self, accept_header="application/json"):
 | 
					    def _get_index_request(self, accept_header="application/json"):
 | 
				
			||||||
        """Helper to set routing arguments."""
 | 
					        """Helper to set routing arguments."""
 | 
				
			||||||
@@ -178,7 +178,7 @@ class LimitsControllerV11Test(BaseLimitTestSuite):
 | 
				
			|||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        """Run before each test."""
 | 
					        """Run before each test."""
 | 
				
			||||||
        BaseLimitTestSuite.setUp(self)
 | 
					        BaseLimitTestSuite.setUp(self)
 | 
				
			||||||
        self.controller = limits.LimitsControllerV11()
 | 
					        self.controller = limits.resource_factory('1.1')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_index_request(self, accept_header="application/json"):
 | 
					    def _get_index_request(self, accept_header="application/json"):
 | 
				
			||||||
        """Helper to set routing arguments."""
 | 
					        """Helper to set routing arguments."""
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -207,7 +207,6 @@ class ServersTest(test.TestCase):
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print res_dict['server']
 | 
					 | 
				
			||||||
        self.assertEqual(res_dict['server']['links'], expected_links)
 | 
					        self.assertEqual(res_dict['server']['links'], expected_links)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_get_server_by_id_with_addresses_xml(self):
 | 
					    def test_get_server_by_id_with_addresses_xml(self):
 | 
				
			||||||
@@ -831,7 +830,6 @@ class ServersTest(test.TestCase):
 | 
				
			|||||||
        req = webob.Request.blank('/v1.0/servers/detail')
 | 
					        req = webob.Request.blank('/v1.0/servers/detail')
 | 
				
			||||||
        req.headers['Accept'] = 'application/xml'
 | 
					        req.headers['Accept'] = 'application/xml'
 | 
				
			||||||
        res = req.get_response(fakes.wsgi_app())
 | 
					        res = req.get_response(fakes.wsgi_app())
 | 
				
			||||||
        print res.body
 | 
					 | 
				
			||||||
        dom = minidom.parseString(res.body)
 | 
					        dom = minidom.parseString(res.body)
 | 
				
			||||||
        for i, server in enumerate(dom.getElementsByTagName('server')):
 | 
					        for i, server in enumerate(dom.getElementsByTagName('server')):
 | 
				
			||||||
            self.assertEqual(server.getAttribute('id'), str(i))
 | 
					            self.assertEqual(server.getAttribute('id'), str(i))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -121,138 +121,3 @@ class ControllerTest(test.TestCase):
 | 
				
			|||||||
        result = request.get_response(self.TestRouter())
 | 
					        result = request.get_response(self.TestRouter())
 | 
				
			||||||
        self.assertEqual(result.status_int, 200)
 | 
					        self.assertEqual(result.status_int, 200)
 | 
				
			||||||
        self.assertEqual(result.headers["Content-Type"], "application/json")
 | 
					        self.assertEqual(result.headers["Content-Type"], "application/json")
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class RequestTest(test.TestCase):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_request_content_type_missing(self):
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123')
 | 
					 | 
				
			||||||
        request.body = "<body />"
 | 
					 | 
				
			||||||
        self.assertRaises(webob.exc.HTTPBadRequest, request.get_content_type)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_request_content_type_unsupported(self):
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123')
 | 
					 | 
				
			||||||
        request.headers["Content-Type"] = "text/html"
 | 
					 | 
				
			||||||
        request.body = "asdf<br />"
 | 
					 | 
				
			||||||
        self.assertRaises(webob.exc.HTTPBadRequest, request.get_content_type)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_request_content_type_with_charset(self):
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123')
 | 
					 | 
				
			||||||
        request.headers["Content-Type"] = "application/json; charset=UTF-8"
 | 
					 | 
				
			||||||
        result = request.get_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/json")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_content_type_from_accept_xml(self):
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123')
 | 
					 | 
				
			||||||
        request.headers["Accept"] = "application/xml"
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/xml")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123')
 | 
					 | 
				
			||||||
        request.headers["Accept"] = "application/json"
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/json")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123')
 | 
					 | 
				
			||||||
        request.headers["Accept"] = "application/xml, application/json"
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/json")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123')
 | 
					 | 
				
			||||||
        request.headers["Accept"] = \
 | 
					 | 
				
			||||||
            "application/json; q=0.3, application/xml; q=0.9"
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/xml")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_content_type_from_query_extension(self):
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123.xml')
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/xml")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123.json')
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/json")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123.invalid')
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/json")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_content_type_accept_and_query_extension(self):
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123.xml')
 | 
					 | 
				
			||||||
        request.headers["Accept"] = "application/json"
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/xml")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_content_type_accept_default(self):
 | 
					 | 
				
			||||||
        request = wsgi.Request.blank('/tests/123.unsupported')
 | 
					 | 
				
			||||||
        request.headers["Accept"] = "application/unsupported1"
 | 
					 | 
				
			||||||
        result = request.best_match_content_type()
 | 
					 | 
				
			||||||
        self.assertEqual(result, "application/json")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SerializerTest(test.TestCase):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_xml(self):
 | 
					 | 
				
			||||||
        input_dict = dict(servers=dict(a=(2, 3)))
 | 
					 | 
				
			||||||
        expected_xml = '<servers><a>(2,3)</a></servers>'
 | 
					 | 
				
			||||||
        serializer = wsgi.Serializer()
 | 
					 | 
				
			||||||
        result = serializer.serialize(input_dict, "application/xml")
 | 
					 | 
				
			||||||
        result = result.replace('\n', '').replace(' ', '')
 | 
					 | 
				
			||||||
        self.assertEqual(result, expected_xml)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_json(self):
 | 
					 | 
				
			||||||
        input_dict = dict(servers=dict(a=(2, 3)))
 | 
					 | 
				
			||||||
        expected_json = '{"servers":{"a":[2,3]}}'
 | 
					 | 
				
			||||||
        serializer = wsgi.Serializer()
 | 
					 | 
				
			||||||
        result = serializer.serialize(input_dict, "application/json")
 | 
					 | 
				
			||||||
        result = result.replace('\n', '').replace(' ', '')
 | 
					 | 
				
			||||||
        self.assertEqual(result, expected_json)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_unsupported_content_type(self):
 | 
					 | 
				
			||||||
        serializer = wsgi.Serializer()
 | 
					 | 
				
			||||||
        self.assertRaises(exception.InvalidContentType, serializer.serialize,
 | 
					 | 
				
			||||||
                          {}, "text/null")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_deserialize_json(self):
 | 
					 | 
				
			||||||
        data = """{"a": {
 | 
					 | 
				
			||||||
                "a1": "1",
 | 
					 | 
				
			||||||
                "a2": "2",
 | 
					 | 
				
			||||||
                "bs": ["1", "2", "3", {"c": {"c1": "1"}}],
 | 
					 | 
				
			||||||
                "d": {"e": "1"},
 | 
					 | 
				
			||||||
                "f": "1"}}"""
 | 
					 | 
				
			||||||
        as_dict = dict(a={
 | 
					 | 
				
			||||||
                'a1': '1',
 | 
					 | 
				
			||||||
                'a2': '2',
 | 
					 | 
				
			||||||
                'bs': ['1', '2', '3', {'c': dict(c1='1')}],
 | 
					 | 
				
			||||||
                'd': {'e': '1'},
 | 
					 | 
				
			||||||
                'f': '1'})
 | 
					 | 
				
			||||||
        metadata = {}
 | 
					 | 
				
			||||||
        serializer = wsgi.Serializer(metadata)
 | 
					 | 
				
			||||||
        self.assertEqual(serializer.deserialize(data, "application/json"),
 | 
					 | 
				
			||||||
                         as_dict)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_deserialize_xml(self):
 | 
					 | 
				
			||||||
        xml = """
 | 
					 | 
				
			||||||
            <a a1="1" a2="2">
 | 
					 | 
				
			||||||
              <bs><b>1</b><b>2</b><b>3</b><b><c c1="1"/></b></bs>
 | 
					 | 
				
			||||||
              <d><e>1</e></d>
 | 
					 | 
				
			||||||
              <f>1</f>
 | 
					 | 
				
			||||||
            </a>
 | 
					 | 
				
			||||||
            """.strip()
 | 
					 | 
				
			||||||
        as_dict = dict(a={
 | 
					 | 
				
			||||||
                'a1': '1',
 | 
					 | 
				
			||||||
                'a2': '2',
 | 
					 | 
				
			||||||
                'bs': ['1', '2', '3', {'c': dict(c1='1')}],
 | 
					 | 
				
			||||||
                'd': {'e': '1'},
 | 
					 | 
				
			||||||
                'f': '1'})
 | 
					 | 
				
			||||||
        metadata = {'application/xml': dict(plurals={'bs': 'b', 'ts': 't'})}
 | 
					 | 
				
			||||||
        serializer = wsgi.Serializer(metadata)
 | 
					 | 
				
			||||||
        self.assertEqual(serializer.deserialize(xml, "application/xml"),
 | 
					 | 
				
			||||||
                         as_dict)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_deserialize_empty_xml(self):
 | 
					 | 
				
			||||||
        xml = """<a></a>"""
 | 
					 | 
				
			||||||
        as_dict = {"a": {}}
 | 
					 | 
				
			||||||
        serializer = wsgi.Serializer()
 | 
					 | 
				
			||||||
        self.assertEqual(serializer.deserialize(xml, "application/xml"),
 | 
					 | 
				
			||||||
                         as_dict)
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ class XmlTests(integrated_helpers._IntegratedTestBase):
 | 
				
			|||||||
    """"Some basic XML sanity checks."""
 | 
					    """"Some basic XML sanity checks."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_namespace_limits(self):
 | 
					    def test_namespace_limits(self):
 | 
				
			||||||
        """/limits should have v1.0 namespace (hasn't changed in 1.1)."""
 | 
					        """/limits should have v1.1 namespace (has changed in 1.1)."""
 | 
				
			||||||
        headers = {}
 | 
					        headers = {}
 | 
				
			||||||
        headers['Accept'] = 'application/xml'
 | 
					        headers['Accept'] = 'application/xml'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,7 +40,7 @@ class XmlTests(integrated_helpers._IntegratedTestBase):
 | 
				
			|||||||
        data = response.read()
 | 
					        data = response.read()
 | 
				
			||||||
        LOG.debug("data: %s" % data)
 | 
					        LOG.debug("data: %s" % data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        prefix = '<limits xmlns="%s"' % common.XML_NS_V10
 | 
					        prefix = '<limits xmlns="%s"' % common.XML_NS_V11
 | 
				
			||||||
        self.assertTrue(data.startswith(prefix))
 | 
					        self.assertTrue(data.startswith(prefix))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_namespace_servers(self):
 | 
					    def test_namespace_servers(self):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user