diff options
| author | Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp> | 2011-08-22 17:01:54 -0700 |
|---|---|---|
| committer | Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp> | 2011-08-22 17:01:54 -0700 |
| commit | 20a58d38232e910f0a5d844caaf506c8d710477f (patch) | |
| tree | efe9d929b23dab1c294652213839ccc2fa1bf5c0 | |
| parent | 0d0c8dfbf29b47aa13e18dd8861bad6ccb10cf12 (diff) | |
| parent | 4ded14d0d8fb4ec1bbc14255e90cbaae0626997f (diff) | |
| download | nova-20a58d38232e910f0a5d844caaf506c8d710477f.tar.gz nova-20a58d38232e910f0a5d844caaf506c8d710477f.tar.xz nova-20a58d38232e910f0a5d844caaf506c8d710477f.zip | |
Merge latest lp:~tpatil/nova/add-options-network-create-os-apis and resolve the conflict
48 files changed, 1913 insertions, 715 deletions
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index e0c1e9d04..3b74fefc9 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -68,6 +68,22 @@ class FaultWrapper(base_wsgi.Middleware): return faults.Fault(exc) +class ProjectMapper(routes.Mapper): + + def resource(self, member_name, collection_name, **kwargs): + if not ('parent_resource' in kwargs): + kwargs['path_prefix'] = '{project_id}/' + else: + parent_resource = kwargs['parent_resource'] + p_collection = parent_resource['collection_name'] + p_member = parent_resource['member_name'] + kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection, + p_member) + routes.Mapper.resource(self, member_name, + collection_name, + **kwargs) + + class APIRouter(base_wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller @@ -81,10 +97,13 @@ class APIRouter(base_wsgi.Router): def __init__(self, ext_mgr=None): self.server_members = {} - mapper = routes.Mapper() + mapper = self._mapper() self._setup_routes(mapper) super(APIRouter, self).__init__(mapper) + def _mapper(self): + return routes.Mapper() + def _setup_routes(self, mapper): raise NotImplementedError(_("You must implement _setup_routes.")) @@ -174,6 +193,9 @@ class APIRouterV10(APIRouter): class APIRouterV11(APIRouter): """Define routes specific to OpenStack API V1.1.""" + def _mapper(self): + return ProjectMapper() + def _setup_routes(self, mapper): self._setup_base_routes(mapper, '1.1') @@ -184,7 +206,7 @@ class APIRouterV11(APIRouter): parent_resource=dict(member_name='image', collection_name='images')) - mapper.connect("metadata", "/images/{image_id}/metadata", + mapper.connect("metadata", "/{project_id}/images/{image_id}/metadata", controller=image_metadata_controller, action='update_all', conditions={"method": ['PUT']}) @@ -196,7 +218,8 @@ class APIRouterV11(APIRouter): parent_resource=dict(member_name='server', collection_name='servers')) - mapper.connect("metadata", "/servers/{server_id}/metadata", + mapper.connect("metadata", + "/{project_id}/servers/{server_id}/metadata", controller=server_metadata_controller, action='update_all', conditions={"method": ['PUT']}) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index d42abe1f8..b6ff1126b 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -28,6 +28,7 @@ from nova import flags from nova import log as logging from nova import utils from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults LOG = logging.getLogger('nova.api.openstack') @@ -55,16 +56,33 @@ class AuthMiddleware(wsgi.Middleware): LOG.warn(msg % locals()) return faults.Fault(webob.exc.HTTPUnauthorized()) - try: - project_id = req.headers["X-Auth-Project-Id"] - except KeyError: - # FIXME(usrleon): It needed only for compatibility - # while osapi clients don't use this header - projects = self.auth.get_projects(user_id) - if projects: - project_id = projects[0].id - else: + # Get all valid projects for the user + projects = self.auth.get_projects(user_id) + if not projects: + return faults.Fault(webob.exc.HTTPUnauthorized()) + + project_id = "" + path_parts = req.path.split('/') + # TODO(wwolf): this v1.1 check will be temporary as + # keystone should be taking this over at some point + if len(path_parts) > 1 and path_parts[1] == 'v1.1': + project_id = path_parts[2] + # Check that the project for project_id exists, and that user + # is authorized to use it + try: + project = self.auth.get_project(project_id) + except exception.ProjectNotFound: + return faults.Fault(webob.exc.HTTPUnauthorized()) + if project_id not in [p.id for p in projects]: return faults.Fault(webob.exc.HTTPUnauthorized()) + else: + # As a fallback, set project_id from the headers, which is the v1.0 + # behavior. As a last resort, be forgiving to the user and set + # project_id based on a valid project of theirs. + try: + project_id = req.headers["X-Auth-Project-Id"] + except KeyError: + project_id = projects[0].id is_admin = self.auth.is_admin(user_id) req.environ['nova.context'] = context.RequestContext(user_id, @@ -95,12 +113,19 @@ class AuthMiddleware(wsgi.Middleware): LOG.warn(msg) return faults.Fault(webob.exc.HTTPUnauthorized(explanation=msg)) + def _get_auth_header(key): + """Ensures that the KeyError returned is meaningful.""" + try: + return req.headers[key] + except KeyError as ex: + raise KeyError(key) try: - username = req.headers['X-Auth-User'] - key = req.headers['X-Auth-Key'] + username = _get_auth_header('X-Auth-User') + key = _get_auth_header('X-Auth-Key') except KeyError as ex: - LOG.warn(_("Could not find %s in request.") % ex) - return faults.Fault(webob.exc.HTTPUnauthorized()) + msg = _("Could not find %s in request.") % ex + LOG.warn(msg) + return faults.Fault(webob.exc.HTTPUnauthorized(explanation=msg)) token, user = self._authorize_user(username, key, req) if user and token: @@ -149,6 +174,16 @@ class AuthMiddleware(wsgi.Middleware): """ ctxt = context.get_admin_context() + project_id = req.headers.get('X-Auth-Project-Id') + if project_id is None: + # If the project_id is not provided in the headers, be forgiving to + # the user and set project_id based on a valid project of theirs. + user = self.auth.get_user_from_access_key(key) + projects = self.auth.get_projects(user.id) + if not projects: + raise webob.exc.HTTPUnauthorized() + project_id = projects[0].id + try: user = self.auth.get_user_from_access_key(key) except exception.NotFound: @@ -162,7 +197,10 @@ class AuthMiddleware(wsgi.Middleware): token_dict['token_hash'] = token_hash token_dict['cdn_management_url'] = '' os_url = req.url - token_dict['server_management_url'] = os_url + token_dict['server_management_url'] = os_url.strip('/') + version = common.get_version_from_href(os_url) + if version == '1.1': + token_dict['server_management_url'] += '/' + project_id token_dict['storage_url'] = '' token_dict['user_id'] = user.id token = self.db.auth_token_create(ctxt, token_dict) diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 44b35c385..40086f778 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -15,8 +15,9 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License -from webob import exc +import webob +from nova import compute from nova import exception from nova import log as logging from nova import network @@ -71,18 +72,22 @@ class FloatingIPController(object): try: floating_ip = self.network_api.get_floating_ip(context, id) except exception.NotFound: - return faults.Fault(exc.HTTPNotFound()) + return faults.Fault(webob.exc.HTTPNotFound()) return _translate_floating_ip_view(floating_ip) def index(self, req): context = req.environ['nova.context'] - floating_ips = self.network_api.list_floating_ips(context) + try: + # FIXME(ja) - why does self.network_api.list_floating_ips raise? + floating_ips = self.network_api.list_floating_ips(context) + except exception.FloatingIpNotFoundForProject: + floating_ips = [] return _translate_floating_ips_view(floating_ips) - def create(self, req): + def create(self, req, body=None): context = req.environ['nova.context'] try: @@ -95,63 +100,67 @@ class FloatingIPController(object): else: raise - return {'allocated': { - "id": ip['id'], - "floating_ip": ip['address']}} + return _translate_floating_ip_view(ip) def delete(self, req, id): context = req.environ['nova.context'] - ip = self.network_api.get_floating_ip(context, id) + floating_ip = self.network_api.get_floating_ip(context, id) - if 'fixed_ip' in ip: - self.disassociate(req, id) + if 'fixed_ip' in floating_ip: + self.network_api.disassociate_floating_ip(context, + floating_ip['address']) - self.network_api.release_floating_ip(context, address=ip['address']) + self.network_api.release_floating_ip(context, + address=floating_ip['address']) + return webob.exc.HTTPAccepted() - return {'released': { - "id": ip['id'], - "floating_ip": ip['address']}} + def _get_ip_by_id(self, context, value): + """Checks that value is id and then returns its address.""" + return self.network_api.get_floating_ip(context, value)['address'] - def associate(self, req, id, body): - """ /floating_ips/{id}/associate fixed ip in body """ - context = req.environ['nova.context'] - floating_ip = self._get_ip_by_id(context, id) - fixed_ip = body['associate_address']['fixed_ip'] +class Floating_ips(extensions.ExtensionDescriptor): + def __init__(self): + self.compute_api = compute.API() + self.network_api = network.API() + super(Floating_ips, self).__init__() - try: - self.network_api.associate_floating_ip(context, - floating_ip, fixed_ip) - except rpc.RemoteError: - raise - - return {'associated': - { - "floating_ip_id": id, - "floating_ip": floating_ip, - "fixed_ip": fixed_ip}} - - def disassociate(self, req, id, body=None): - """ POST /floating_ips/{id}/disassociate """ + def _add_floating_ip(self, input_dict, req, instance_id): + """Associate floating_ip to an instance.""" context = req.environ['nova.context'] - floating_ip = self.network_api.get_floating_ip(context, id) - address = floating_ip['address'] - fixed_ip = floating_ip['fixed_ip']['address'] try: - self.network_api.disassociate_floating_ip(context, address) - except rpc.RemoteError: - raise + address = input_dict['addFloatingIp']['address'] + except TypeError: + msg = _("Missing parameter dict") + raise webob.exc.HTTPBadRequest(explanation=msg) + except KeyError: + msg = _("Address not specified") + raise webob.exc.HTTPBadRequest(explanation=msg) - return {'disassociated': {'floating_ip': address, - 'fixed_ip': fixed_ip}} + self.compute_api.associate_floating_ip(context, instance_id, address) - def _get_ip_by_id(self, context, value): - """Checks that value is id and then returns its address.""" - return self.network_api.get_floating_ip(context, value)['address'] + return webob.Response(status_int=202) + def _remove_floating_ip(self, input_dict, req, instance_id): + """Dissociate floating_ip from an instance.""" + context = req.environ['nova.context'] + + try: + address = input_dict['removeFloatingIp']['address'] + except TypeError: + msg = _("Missing parameter dict") + raise webob.exc.HTTPBadRequest(explanation=msg) + except KeyError: + msg = _("Address not specified") + raise webob.exc.HTTPBadRequest(explanation=msg) + + floating_ip = self.network_api.get_floating_ip_by_ip(context, address) + if 'fixed_ip' in floating_ip: + self.network_api.disassociate_floating_ip(context, address) + + return webob.Response(status_int=202) -class Floating_ips(extensions.ExtensionDescriptor): def get_name(self): return "Floating_ips" @@ -172,9 +181,18 @@ class Floating_ips(extensions.ExtensionDescriptor): res = extensions.ResourceExtension('os-floating-ips', FloatingIPController(), - member_actions={ - 'associate': 'POST', - 'disassociate': 'POST'}) + member_actions={}) resources.append(res) return resources + + def get_actions(self): + """Return the actions the extension adds, as required by contract.""" + actions = [ + extensions.ActionExtension("servers", "addFloatingIp", + self._add_floating_ip), + extensions.ActionExtension("servers", "removeFloatingIp", + self._remove_floating_ip), + ] + + return actions diff --git a/nova/api/openstack/contrib/security_groups.py b/nova/api/openstack/contrib/security_groups.py index 6c57fbb51..1fd64f3b8 100644 --- a/nova/api/openstack/contrib/security_groups.py +++ b/nova/api/openstack/contrib/security_groups.py @@ -25,10 +25,11 @@ from nova import db from nova import exception from nova import flags from nova import log as logging +from nova import rpc from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import wsgi - +from nova.compute import power_state from xml.dom import minidom @@ -73,33 +74,28 @@ class SecurityGroupController(object): context, rule)] return security_group - def show(self, req, id): - """Return data about the given security group.""" - context = req.environ['nova.context'] + def _get_security_group(self, context, id): try: id = int(id) security_group = db.security_group_get(context, id) except ValueError: - msg = _("Security group id is not integer") - return exc.HTTPBadRequest(explanation=msg) + msg = _("Security group id should be integer") + raise exc.HTTPBadRequest(explanation=msg) except exception.NotFound as exp: - return exc.HTTPNotFound(explanation=unicode(exp)) + raise exc.HTTPNotFound(explanation=unicode(exp)) + return security_group + def show(self, req, id): + """Return data about the given security group.""" + context = req.environ['nova.context'] + security_group = self._get_security_group(context, id) return {'security_group': self._format_security_group(context, security_group)} def delete(self, req, id): """Delete a security group.""" context = req.environ['nova.context'] - try: - id = int(id) - security_group = db.security_group_get(context, id) - except ValueError: - msg = _("Security group id is not integer") - return exc.HTTPBadRequest(explanation=msg) - except exception.SecurityGroupNotFound as exp: - return exc.HTTPNotFound(explanation=unicode(exp)) - + security_group = self._get_security_group(context, id) LOG.audit(_("Delete security group %s"), id, context=context) db.security_group_destroy(context, security_group.id) @@ -226,9 +222,9 @@ class SecurityGroupRulesController(SecurityGroupController): security_group_rule = db.security_group_rule_create(context, values) self.compute_api.trigger_security_group_rules_refresh(context, - security_group_id=security_group['id']) + security_group_id=security_group['id']) - return {'security_group_rule': self._format_security_group_rule( + return {"security_group_rule": self._format_security_group_rule( context, security_group_rule)} @@ -336,6 +332,11 @@ class SecurityGroupRulesController(SecurityGroupController): class Security_groups(extensions.ExtensionDescriptor): + + def __init__(self): + self.compute_api = compute.API() + super(Security_groups, self).__init__() + def get_name(self): return "SecurityGroups" @@ -351,6 +352,82 @@ class Security_groups(extensions.ExtensionDescriptor): def get_updated(self): return "2011-07-21T00:00:00+00:00" + def _addSecurityGroup(self, input_dict, req, instance_id): + context = req.environ['nova.context'] + + try: + body = input_dict['addSecurityGroup'] + group_name = body['name'] + instance_id = int(instance_id) + except ValueError: + msg = _("Server id should be integer") + raise exc.HTTPBadRequest(explanation=msg) + except TypeError: + msg = _("Missing parameter dict") + raise webob.exc.HTTPBadRequest(explanation=msg) + except KeyError: + msg = _("Security group not specified") + raise webob.exc.HTTPBadRequest(explanation=msg) + + if not group_name or group_name.strip() == '': + msg = _("Security group name cannot be empty") + raise webob.exc.HTTPBadRequest(explanation=msg) + + try: + self.compute_api.add_security_group(context, instance_id, + group_name) + except exception.SecurityGroupNotFound as exp: + return exc.HTTPNotFound(explanation=unicode(exp)) + except exception.InstanceNotFound as exp: + return exc.HTTPNotFound(explanation=unicode(exp)) + except exception.Invalid as exp: + return exc.HTTPBadRequest(explanation=unicode(exp)) + + return exc.HTTPAccepted() + + def _removeSecurityGroup(self, input_dict, req, instance_id): + context = req.environ['nova.context'] + + try: + body = input_dict['removeSecurityGroup'] + group_name = body['name'] + instance_id = int(instance_id) + except ValueError: + msg = _("Server id should be integer") + raise exc.HTTPBadRequest(explanation=msg) + except TypeError: + msg = _("Missing parameter dict") + raise webob.exc.HTTPBadRequest(explanation=msg) + except KeyError: + msg = _("Security group not specified") + raise webob.exc.HTTPBadRequest(explanation=msg) + + if not group_name or group_name.strip() == '': + msg = _("Security group name cannot be empty") + raise webob.exc.HTTPBadRequest(explanation=msg) + + try: + self.compute_api.remove_security_group(context, instance_id, + group_name) + except exception.SecurityGroupNotFound as exp: + return exc.HTTPNotFound(explanation=unicode(exp)) + except exception.InstanceNotFound as exp: + return exc.HTTPNotFound(explanation=unicode(exp)) + except exception.Invalid as exp: + return exc.HTTPBadRequest(explanation=unicode(exp)) + + return exc.HTTPAccepted() + + def get_actions(self): + """Return the actions the extensions adds""" + actions = [ + extensions.ActionExtension("servers", "addSecurityGroup", + self._addSecurityGroup), + extensions.ActionExtension("servers", "removeSecurityGroup", + self._removeSecurityGroup) + ] + return actions + def get_resources(self): resources = [] diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 42b553674..4b4a1b0c3 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -111,8 +111,16 @@ class CreateInstanceHelper(object): if personality: injected_files = self._get_injected_files(personality) - requested_networks = server_dict.get('networks') + sg_names = [] + security_groups = server_dict.get('security_groups') + if security_groups is not None: + sg_names = [sg['name'] for sg in security_groups if sg.get('name')] + if not sg_names: + sg_names.append('default') + + sg_names = list(set(sg_names)) + requested_networks = server_dict.get('networks') if requested_networks is not None: requested_networks = self._get_requested_networks( requested_networks) @@ -164,6 +172,8 @@ class CreateInstanceHelper(object): key_name=key_name, key_data=key_data, metadata=server_dict.get('metadata', {}), + access_ip_v4=server_dict.get('accessIPv4'), + access_ip_v6=server_dict.get('accessIPv6'), injected_files=injected_files, admin_password=password, zone_blob=zone_blob, @@ -171,6 +181,7 @@ class CreateInstanceHelper(object): min_count=min_count, max_count=max_count, requested_networks=requested_networks, + security_group=sg_names, user_data=user_data, availability_zone=availability_zone)) except quota.QuotaError as error: @@ -181,6 +192,8 @@ class CreateInstanceHelper(object): except exception.FlavorNotFound as error: msg = _("Invalid flavorRef provided.") raise exc.HTTPBadRequest(explanation=msg) + except exception.SecurityGroupNotFound as error: + raise exc.HTTPBadRequest(explanation=unicode(error)) except RemoteError as err: msg = "%(err_type)s: %(err_msg)s" % \ {'err_type': err.exc_type, 'err_msg': err.value} @@ -503,7 +516,8 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): server = {} server_node = self.find_first_child_named(node, 'server') - attributes = ["name", "imageRef", "flavorRef", "adminPass"] + attributes = ["name", "imageRef", "flavorRef", "adminPass", + "accessIPv4", "accessIPv6"] for attr in attributes: if server_node.getAttribute(attr): server[attr] = server_node.getAttribute(attr) @@ -520,6 +534,10 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): if networks is not None: server["networks"] = networks + security_groups = self._extract_security_groups(server_node) + if security_groups is not None: + server["security_groups"] = security_groups + return server def _extract_personality(self, server_node): @@ -553,3 +571,18 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): return networks else: return None + + def _extract_security_groups(self, server_node): + """Marshal the security_groups attribute of a parsed request""" + node = self.find_first_child_named(server_node, "security_groups") + if node is not None: + security_groups = [] + for sg_node in self.find_children_named(node, "security_group"): + item = {} + name_node = self.find_first_child_named(sg_node, "name") + if name_node: + item["name"] = self.extract_text(name_node) + security_groups.append(item) + return security_groups + else: + return None diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index bb407a045..efede945f 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -29,6 +29,7 @@ from nova import exception from nova import flags from nova import log as logging from nova import wsgi as base_wsgi +import nova.api.openstack from nova.api.openstack import common from nova.api.openstack import faults from nova.api.openstack import wsgi @@ -220,12 +221,13 @@ class ExtensionMiddleware(base_wsgi.Middleware): for action in ext_mgr.get_actions(): if not action.collection in action_resources.keys(): resource = ActionExtensionResource(application) - mapper.connect("/%s/:(id)/action.:(format)" % + mapper.connect("/:(project_id)/%s/:(id)/action.:(format)" % action.collection, action='action', controller=resource, conditions=dict(method=['POST'])) - mapper.connect("/%s/:(id)/action" % action.collection, + mapper.connect("/:(project_id)/%s/:(id)/action" % + action.collection, action='action', controller=resource, conditions=dict(method=['POST'])) @@ -258,7 +260,7 @@ class ExtensionMiddleware(base_wsgi.Middleware): ext_mgr = ExtensionManager(FLAGS.osapi_extensions_path) self.ext_mgr = ext_mgr - mapper = routes.Mapper() + mapper = nova.api.openstack.ProjectMapper() serializer = wsgi.ResponseSerializer( {'application/xml': ExtensionsXMLSerializer()}) @@ -269,13 +271,17 @@ class ExtensionMiddleware(base_wsgi.Middleware): if resource.serializer is None: resource.serializer = serializer - mapper.resource(resource.collection, resource.collection, + kargs = dict( controller=wsgi.Resource( resource.controller, resource.deserializer, resource.serializer), collection=resource.collection_actions, - member=resource.member_actions, - parent_resource=resource.parent) + member=resource.member_actions) + + if resource.parent: + kargs['parent_resource'] = resource.parent + + mapper.resource(resource.collection, resource.collection, **kargs) # extended actions action_resources = self._action_ext_resources(application, ext_mgr, diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index b4bda68d4..fd36060da 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -72,7 +72,8 @@ class ControllerV11(Controller): def _get_view_builder(self, req): base_url = req.application_url - return views.flavors.ViewBuilderV11(base_url) + project_id = getattr(req.environ['nova.context'], 'project_id', '') + return views.flavors.ViewBuilderV11(base_url, project_id) class FlavorXMLSerializer(wsgi.XMLDictSerializer): diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 0aabb9e56..1c8fc10c9 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -166,10 +166,11 @@ class ControllerV10(Controller): class ControllerV11(Controller): """Version 1.1 specific controller logic.""" - def get_builder(self, request): + def get_builder(self, req): """Property to get the ViewBuilder class we need to use.""" - base_url = request.application_url - return images_view.ViewBuilderV11(base_url) + base_url = req.application_url + project_id = getattr(req.environ['nova.context'], 'project_id', '') + return images_view.ViewBuilderV11(base_url, project_id) def index(self, req): """Return an index listing of images available to the request. diff --git a/nova/api/openstack/schemas/v1.1/server.rng b/nova/api/openstack/schemas/v1.1/server.rng new file mode 100644 index 000000000..dbd169a83 --- /dev/null +++ b/nova/api/openstack/schemas/v1.1/server.rng @@ -0,0 +1,50 @@ +<element name="server" ns="http://docs.openstack.org/compute/api/v1.1" + xmlns="http://relaxng.org/ns/structure/1.0"> + <attribute name="name"> <text/> </attribute> + <attribute name="id"> <text/> </attribute> + <attribute name="uuid"> <text/> </attribute> + <attribute name="updated"> <text/> </attribute> + <attribute name="created"> <text/> </attribute> + <attribute name="hostId"> <text/> </attribute> + <attribute name="accessIPv4"> <text/> </attribute> + <attribute name="accessIPv6"> <text/> </attribute> + <attribute name="status"> <text/> </attribute> + <optional> + <attribute name="progress"> <text/> </attribute> + </optional> + <optional> + <attribute name="adminPass"> <text/> </attribute> + </optional> + <zeroOrMore> + <externalRef href="../atom-link.rng"/> + </zeroOrMore> + <element name="image"> + <attribute name="id"> <text/> </attribute> + <externalRef href="../atom-link.rng"/> + </element> + <element name="flavor"> + <attribute name="id"> <text/> </attribute> + <externalRef href="../atom-link.rng"/> + </element> + <element name="metadata"> + <zeroOrMore> + <element name="meta"> + <attribute name="key"> <text/> </attribute> + <text/> + </element> + </zeroOrMore> + </element> + <element name="addresses"> + <zeroOrMore> + <element name="network"> + <attribute name="id"> <text/> </attribute> + <zeroOrMore> + <element name="ip"> + <attribute name="version"> <text/> </attribute> + <attribute name="addr"> <text/> </attribute> + </element> + </zeroOrMore> + </element> + </zeroOrMore> + </element> +</element> diff --git a/nova/api/openstack/schemas/v1.1/servers.rng b/nova/api/openstack/schemas/v1.1/servers.rng new file mode 100644 index 000000000..4e2bb8853 --- /dev/null +++ b/nova/api/openstack/schemas/v1.1/servers.rng @@ -0,0 +1,6 @@ +<element name="servers" xmlns="http://relaxng.org/ns/structure/1.0" + ns="http://docs.openstack.org/compute/api/v1.1"> + <zeroOrMore> + <externalRef href="server.rng"/> + </zeroOrMore> +</element> diff --git a/nova/api/openstack/schemas/v1.1/servers_index.rng b/nova/api/openstack/schemas/v1.1/servers_index.rng new file mode 100644 index 000000000..768f0912d --- /dev/null +++ b/nova/api/openstack/schemas/v1.1/servers_index.rng @@ -0,0 +1,12 @@ +<element name="servers" ns="http://docs.openstack.org/compute/api/v1.1" + xmlns="http://relaxng.org/ns/structure/1.0"> + <zeroOrMore> + <element name="server"> + <attribute name="name"> <text/> </attribute> + <attribute name="id"> <text/> </attribute> + <zeroOrMore> + <externalRef href="../atom-link.rng"/> + </zeroOrMore> + </element> + </zeroOrMore> +</element> diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 41e63ec3c..553357404 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -163,7 +163,7 @@ class Controller(object): @scheduler_api.redirect_handler def update(self, req, id, body): - """Update server name then pass on to version-specific controller""" + """Update server then pass on to version-specific controller""" if len(req.body) == 0: raise exc.HTTPUnprocessableEntity() @@ -178,6 +178,14 @@ class Controller(object): self.helper._validate_server_name(name) update_dict['display_name'] = name.strip() + if 'accessIPv4' in body['server']: + access_ipv4 = body['server']['accessIPv4'] + update_dict['access_ip_v4'] = access_ipv4.strip() + + if 'accessIPv6' in body['server']: + access_ipv6 = body['server']['accessIPv6'] + update_dict['access_ip_v6'] = access_ipv6.strip() + try: self.compute_api.update(ctxt, id, **update_dict) except exception.NotFound: @@ -642,14 +650,16 @@ class ControllerV11(Controller): return common.get_id_from_href(flavor_ref) def _build_view(self, req, instance, is_detail=False): + project_id = getattr(req.environ['nova.context'], 'project_id', '') base_url = req.application_url flavor_builder = nova.api.openstack.views.flavors.ViewBuilderV11( - base_url) + base_url, project_id) image_builder = nova.api.openstack.views.images.ViewBuilderV11( - base_url) + base_url, project_id) addresses_builder = nova.api.openstack.views.addresses.ViewBuilderV11() builder = nova.api.openstack.views.servers.ViewBuilderV11( - addresses_builder, flavor_builder, image_builder, base_url) + addresses_builder, flavor_builder, image_builder, + base_url, project_id) return builder.build(instance, is_detail=is_detail) @@ -837,6 +847,10 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): node.setAttribute('created', str(server['created'])) node.setAttribute('updated', str(server['updated'])) node.setAttribute('status', server['status']) + if 'accessIPv4' in server: + node.setAttribute('accessIPv4', str(server['accessIPv4'])) + if 'accessIPv6' in server: + node.setAttribute('accessIPv6', str(server['accessIPv6'])) if 'progress' in server: node.setAttribute('progress', str(server['progress'])) diff --git a/nova/api/openstack/views/flavors.py b/nova/api/openstack/views/flavors.py index 0403ece1b..aea34b424 100644 --- a/nova/api/openstack/views/flavors.py +++ b/nova/api/openstack/views/flavors.py @@ -15,6 +15,9 @@ # License for the specific language governing permissions and limitations # under the License. +import os.path + + from nova.api.openstack import common @@ -59,11 +62,12 @@ class ViewBuilder(object): class ViewBuilderV11(ViewBuilder): """Openstack API v1.1 flavors view builder.""" - def __init__(self, base_url): + def __init__(self, base_url, project_id=""): """ :param base_url: url of the root wsgi application """ self.base_url = base_url + self.project_id = project_id def _build_extra(self, flavor_obj): flavor_obj["links"] = self._build_links(flavor_obj) @@ -88,11 +92,10 @@ class ViewBuilderV11(ViewBuilder): def generate_href(self, flavor_id): """Create an url that refers to a specific flavor id.""" - return "%s/flavors/%s" % (self.base_url, flavor_id) + return os.path.join(self.base_url, self.project_id, + "flavors", str(flavor_id)) def generate_bookmark(self, flavor_id): """Create an url that refers to a specific flavor id.""" - return "%s/flavors/%s" % ( - common.remove_version_from_href(self.base_url), - flavor_id, - ) + return os.path.join(common.remove_version_from_href(self.base_url), + self.project_id, "flavors", str(flavor_id)) diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 912303d14..21f1b2d3e 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -23,9 +23,10 @@ from nova.api.openstack import common class ViewBuilder(object): """Base class for generating responses to OpenStack API image requests.""" - def __init__(self, base_url): + def __init__(self, base_url, project_id=""): """Initialize new `ViewBuilder`.""" - self._url = base_url + self.base_url = base_url + self.project_id = project_id def _format_dates(self, image): """Update all date fields to ensure standardized formatting.""" @@ -54,7 +55,7 @@ class ViewBuilder(object): def generate_href(self, image_id): """Return an href string pointing to this object.""" - return os.path.join(self._url, "images", str(image_id)) + return os.path.join(self.base_url, "images", str(image_id)) def build(self, image_obj, detail=False): """Return a standardized image structure for display by the API.""" @@ -117,6 +118,11 @@ class ViewBuilderV11(ViewBuilder): except KeyError: return + def generate_href(self, image_id): + """Return an href string pointing to this object.""" + return os.path.join(self.base_url, self.project_id, + "images", str(image_id)) + def build(self, image_obj, detail=False): """Return a standardized image structure for display by the API.""" image = ViewBuilder.build(self, image_obj, detail) @@ -142,5 +148,5 @@ class ViewBuilderV11(ViewBuilder): def generate_bookmark(self, image_id): """Create an url that refers to a specific flavor id.""" - return os.path.join(common.remove_version_from_href(self._url), - "images", str(image_id)) + return os.path.join(common.remove_version_from_href(self.base_url), + self.project_id, "images", str(image_id)) diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index edc328129..465287adc 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -128,11 +128,12 @@ class ViewBuilderV10(ViewBuilder): class ViewBuilderV11(ViewBuilder): """Model an Openstack API V1.0 server response.""" def __init__(self, addresses_builder, flavor_builder, image_builder, - base_url): + base_url, project_id=""): ViewBuilder.__init__(self, addresses_builder) self.flavor_builder = flavor_builder self.image_builder = image_builder self.base_url = base_url + self.project_id = project_id def _build_detail(self, inst): response = super(ViewBuilderV11, self)._build_detail(inst) @@ -143,6 +144,10 @@ class ViewBuilderV11(ViewBuilder): response['server']['progress'] = 100 elif response['server']['status'] == "BUILD": response['server']['progress'] = 0 + + response['server']['accessIPv4'] = inst.get('access_ip_v4') or "" + response['server']['accessIPv6'] = inst.get('access_ip_v6') or "" + return response def _build_image(self, response, inst): @@ -202,9 +207,10 @@ class ViewBuilderV11(ViewBuilder): def generate_href(self, server_id): """Create an url that refers to a specific server id.""" - return os.path.join(self.base_url, "servers", str(server_id)) + return os.path.join(self.base_url, self.project_id, + "servers", str(server_id)) def generate_bookmark(self, server_id): """Create an url that refers to a specific flavor id.""" return os.path.join(common.remove_version_from_href(self.base_url), - "servers", str(server_id)) + self.project_id, "servers", str(server_id)) diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 0eb47044e..dc0f1b93e 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -486,6 +486,10 @@ class Resource(wsgi.Application): msg = _("Malformed request body") return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg)) + project_id = args.pop("project_id", None) + if 'nova.context' in request.environ and project_id: + request.environ['nova.context'].project_id = project_id + try: action_result = self.dispatch(request, action, args) except webob.exc.HTTPException as ex: diff --git a/nova/compute/api.py b/nova/compute/api.py index faa44928a..7de91584f 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -163,7 +163,8 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata=None, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None, requested_networks=None): + reservation_id=None, access_ip_v4=None, access_ip_v6=None, + requested_networks=None): """Verify all the input parameters regardless of the provisioning strategy being performed.""" @@ -258,6 +259,8 @@ class API(base.Base): 'key_data': key_data, 'locked': False, 'metadata': metadata, + 'access_ip_v4': access_ip_v4, + 'access_ip_v6': access_ip_v6, 'availability_zone': availability_zone, 'os_type': os_type, 'architecture': architecture, @@ -450,6 +453,7 @@ class API(base.Base): availability_zone=None, user_data=None, metadata=None, injected_files=None, admin_password=None, zone_blob=None, reservation_id=None, block_device_mapping=None, + access_ip_v4=None, access_ip_v6=None, requested_networks=None): """Provision the instances by passing the whole request to the Scheduler for execution. Returns a Reservation ID @@ -466,7 +470,8 @@ class API(base.Base): key_name, key_data, security_group, availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, - reservation_id, requested_networks) + reservation_id, access_ip_v4, access_ip_v6, + requested_networks) self._ask_scheduler_to_create_instance(context, base_options, instance_type, zone_blob, @@ -485,6 +490,7 @@ class API(base.Base): availability_zone=None, user_data=None, metadata=None, injected_files=None, admin_password=None, zone_blob=None, reservation_id=None, block_device_mapping=None, + access_ip_v4=None, access_ip_v6=None, requested_networks=None): """ Provision the instances by sending off a series of single @@ -509,7 +515,8 @@ class API(base.Base): key_name, key_data, security_group, availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, - reservation_id, requested_networks) + reservation_id, access_ip_v4, access_ip_v6, + requested_networks) block_device_mapping = block_device_mapping or [] instances = [] @@ -627,6 +634,78 @@ class API(base.Base): self.db.queue_get_for(context, FLAGS.compute_topic, host), {'method': 'refresh_provider_fw_rules', 'args': {}}) + def _is_security_group_associated_with_server(self, security_group, + instance_id): + """Check if the security group is already associated + with the instance. If Yes, return True. + """ + + if not security_group: + return False + + instances = security_group.get('instances') + if not instances: + return False + + inst_id = None + for inst_id in (instance['id'] for instance in instances \ + if instance_id == instance['id']): + return True + + return False + + def add_security_group(self, context, instance_id, security_group_name): + """Add security group to the instance""" + security_group = db.security_group_get_by_name(context, + context.project_id, + security_group_name) + # check if the server exists + inst = db.instance_get(context, instance_id) + #check if the security group is associated with the server + if self._is_security_group_associated_with_server(security_group, + instance_id): + raise exception.SecurityGroupExistsForInstance( + security_group_id=security_group['id'], + instance_id=instance_id) + + #check if the instance is in running state + if inst['state'] != power_state.RUNNING: + raise exception.InstanceNotRunning(instance_id=instance_id) + + db.instance_add_security_group(context.elevated(), + instance_id, + security_group['id']) + rpc.cast(context, + db.queue_get_for(context, FLAGS.compute_topic, inst['host']), + {"method": "refresh_security_group_rules", + "args": {"security_group_id": security_group['id']}}) + + def remove_security_group(self, context, instance_id, security_group_name): + """Remove the security group associated with the instance""" + security_group = db.security_group_get_by_name(context, + context.project_id, + security_group_name) + # check if the server exists + inst = db.instance_get(context, instance_id) + #check if the security group is associated with the server + if not self._is_security_group_associated_with_server(security_group, + instance_id): + raise exception.SecurityGroupNotExistsForInstance( + security_group_id=security_group['id'], + instance_id=instance_id) + + #check if the instance is in running state + if inst['state'] != power_state.RUNNING: + raise exception.InstanceNotRunning(instance_id=instance_id) + + db.instance_remove_security_group(context.elevated(), + instance_id, + security_group['id']) + rpc.cast(context, + db.queue_get_for(context, FLAGS.compute_topic, inst['host']), + {"method": "refresh_security_group_rules", + "args": {"security_group_id": security_group['id']}}) + @scheduler_api.reroute_compute("update") def update(self, context, instance_id, **kwargs): """Updates the instance in the datastore. diff --git a/nova/db/api.py b/nova/db/api.py index aa1d0424e..7268cde7f 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -569,6 +569,12 @@ def instance_add_security_group(context, instance_id, security_group_id): security_group_id) +def instance_remove_security_group(context, instance_id, security_group_id): + """Disassociate the given security group from the given instance.""" + return IMPL.instance_remove_security_group(context, instance_id, + security_group_id) + + def instance_action_create(context, values): """Create an instance action from the values dictionary.""" return IMPL.instance_action_create(context, values) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 4a65c179e..e5fffa784 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1515,6 +1515,19 @@ def instance_add_security_group(context, instance_id, security_group_id): @require_context +def instance_remove_security_group(context, instance_id, security_group_id): + """Disassociate the given security group from the given instance""" + session = get_session() + + session.query(models.SecurityGroupInstanceAssociation).\ + filter_by(instance_id=instance_id).\ + filter_by(security_group_id=security_group_id).\ + update({'deleted': True, + 'deleted_at': utils.utcnow(), + 'updated_at': literal_column('updated_at')}) + + +@require_context def instance_action_create(context, values): """Create an instance action from the values dictionary.""" action_ref = models.InstanceActions() @@ -2515,6 +2528,7 @@ def security_group_get(context, security_group_id, session=None): filter_by(deleted=can_read_deleted(context),).\ filter_by(id=security_group_id).\ options(joinedload_all('rules')).\ + options(joinedload_all('instances')).\ first() else: result = session.query(models.SecurityGroup).\ @@ -2522,6 +2536,7 @@ def security_group_get(context, security_group_id, session=None): filter_by(id=security_group_id).\ filter_by(project_id=context.project_id).\ options(joinedload_all('rules')).\ + options(joinedload_all('instances')).\ first() if not result: raise exception.SecurityGroupNotFound( diff --git a/nova/db/sqlalchemy/migrate_repo/versions/039_add_instances_accessip.py b/nova/db/sqlalchemy/migrate_repo/versions/039_add_instances_accessip.py new file mode 100644 index 000000000..39f0dd6ce --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/039_add_instances_accessip.py @@ -0,0 +1,48 @@ +# Copyright 2011 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import Column, Integer, MetaData, Table, String + +meta = MetaData() + +accessIPv4 = Column( + 'access_ip_v4', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + nullable=True) + +accessIPv6 = Column( + 'access_ip_v6', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + nullable=True) + +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + instances.create_column(accessIPv4) + instances.create_column(accessIPv6) + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + meta.bind = migrate_engine + instances.drop_column('access_ip_v4') + instances.drop_column('access_ip_v6') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/039_add_uuid_to_networks.py b/nova/db/sqlalchemy/migrate_repo/versions/040_add_uuid_to_networks.py index 38c543d51..38c543d51 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/039_add_uuid_to_networks.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/040_add_uuid_to_networks.py diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index bb69b26e1..19dc3302e 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -231,6 +231,11 @@ class Instance(BASE, NovaBase): root_device_name = Column(String(255)) + # User editable field meant to represent what ip should be used + # to connect to the instance + access_ip_v4 = Column(String(255)) + access_ip_v6 = Column(String(255)) + # TODO(vish): see Ewan's email about state improvements, probably # should be in a driver base class or some such # vmstate_state = running, halted, suspended, paused diff --git a/nova/exception.py b/nova/exception.py index 262122990..66740019b 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -563,6 +563,16 @@ class SecurityGroupNotFoundForRule(SecurityGroupNotFound): message = _("Security group with rule %(rule_id)s not found.") +class SecurityGroupExistsForInstance(Invalid): + message = _("Security group %(security_group_id)s is already associated" + " with the instance %(instance_id)s") + + +class SecurityGroupNotExistsForInstance(Invalid): + message = _("Security group %(security_group_id)s is not associated with" + " the instance %(instance_id)s") + + class MigrationNotFound(NotFound): message = _("Migration %(migration_id)s could not be found.") diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py index 458434a81..7d44489a1 100644 --- a/nova/tests/api/openstack/__init__.py +++ b/nova/tests/api/openstack/__init__.py @@ -44,14 +44,15 @@ class RateLimitingMiddlewareTest(test.TestCase): action = middleware.get_action_name(req) self.assertEqual(action, action_name) - verify('PUT', '/servers/4', 'PUT') - verify('DELETE', '/servers/4', 'DELETE') - verify('POST', '/images/4', 'POST') - verify('POST', '/servers/4', 'POST servers') - verify('GET', '/foo?a=4&changes-since=never&b=5', 'GET changes-since') - verify('GET', '/foo?a=4&monkeys-since=never&b=5', None) - verify('GET', '/servers/4', None) - verify('HEAD', '/servers/4', None) + verify('PUT', '/fake/servers/4', 'PUT') + verify('DELETE', '/fake/servers/4', 'DELETE') + verify('POST', '/fake/images/4', 'POST') + verify('POST', '/fake/servers/4', 'POST servers') + verify('GET', '/fake/foo?a=4&changes-since=never&b=5', + 'GET changes-since') + verify('GET', '/fake/foo?a=4&monkeys-since=never&b=5', None) + verify('GET', '/fake/servers/4', None) + verify('HEAD', '/fake/servers/4', None) def exhaust(self, middleware, method, url, username, times): req = Request.blank(url, dict(REQUEST_METHOD=method), @@ -67,13 +68,13 @@ class RateLimitingMiddlewareTest(test.TestCase): def test_single_action(self): middleware = RateLimitingMiddleware(simple_wsgi) - self.exhaust(middleware, 'DELETE', '/servers/4', 'usr1', 100) - self.exhaust(middleware, 'DELETE', '/servers/4', 'usr2', 100) + self.exhaust(middleware, 'DELETE', '/fake/servers/4', 'usr1', 100) + self.exhaust(middleware, 'DELETE', '/fake/servers/4', 'usr2', 100) def test_POST_servers_action_implies_POST_action(self): middleware = RateLimitingMiddleware(simple_wsgi) - self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) - self.exhaust(middleware, 'POST', '/images/4', 'usr2', 10) + self.exhaust(middleware, 'POST', '/fake/servers/4', 'usr1', 10) + self.exhaust(middleware, 'POST', '/fake/images/4', 'usr2', 10) self.assertTrue(set(middleware.limiter._levels) == \ set(['usr1:POST', 'usr1:POST servers', 'usr2:POST'])) @@ -81,11 +82,11 @@ class RateLimitingMiddlewareTest(test.TestCase): middleware = RateLimitingMiddleware(simple_wsgi) # Use up all of our "POST" allowance for the minute, 5 times for i in range(5): - self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) + self.exhaust(middleware, 'POST', '/fake/servers/4', 'usr1', 10) # Reset the 'POST' action counter. del middleware.limiter._levels['usr1:POST'] # All 50 daily "POST servers" actions should be all used up - self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 0) + self.exhaust(middleware, 'POST', '/fake/servers/4', 'usr1', 0) def test_proxy_ctor_works(self): middleware = RateLimitingMiddleware(simple_wsgi) diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index 38415179a..e5eed14fe 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -120,7 +120,7 @@ class CreateserverextTest(test.TestCase): return {'server': server} def _get_create_request_json(self, body_dict): - req = webob.Request.blank('/v1.1/os-create-server-ext') + req = webob.Request.blank('/v1.1/123/os-create-server-ext') req.headers['Content-Type'] = 'application/json' req.method = 'POST' req.body = json.dumps(body_dict) @@ -164,7 +164,7 @@ class CreateserverextTest(test.TestCase): return ''.join(body_parts) def _get_create_request_xml(self, body_dict): - req = webob.Request.blank('/v1.1/os-create-server-ext') + req = webob.Request.blank('/v1.1/123/os-create-server-ext') req.content_type = 'application/xml' req.accept = 'application/xml' req.method = 'POST' diff --git a/nova/tests/api/openstack/contrib/test_floating_ips.py b/nova/tests/api/openstack/contrib/test_floating_ips.py index 704d06582..568faf867 100644 --- a/nova/tests/api/openstack/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/contrib/test_floating_ips.py @@ -17,6 +17,7 @@ import json import stubout import webob +from nova import compute from nova import context from nova import db from nova import test @@ -30,6 +31,11 @@ from nova.api.openstack.contrib.floating_ips import _translate_floating_ip_view def network_api_get_floating_ip(self, context, id): return {'id': 1, 'address': '10.10.10.10', + 'fixed_ip': None} + + +def network_api_get_floating_ip_by_ip(self, context, address): + return {'id': 1, 'address': '10.10.10.10', 'fixed_ip': {'address': '11.0.0.1'}} @@ -50,7 +56,7 @@ def network_api_release(self, context, address): pass -def network_api_associate(self, context, floating_ip, fixed_ip): +def compute_api_associate(self, context, instance_id, floating_ip): pass @@ -78,14 +84,16 @@ class FloatingIpTest(test.TestCase): fakes.stub_out_rate_limiting(self.stubs) self.stubs.Set(network.api.API, "get_floating_ip", network_api_get_floating_ip) + self.stubs.Set(network.api.API, "get_floating_ip_by_ip", + network_api_get_floating_ip) self.stubs.Set(network.api.API, "list_floating_ips", network_api_list_floating_ips) self.stubs.Set(network.api.API, "allocate_floating_ip", network_api_allocate) self.stubs.Set(network.api.API, "release_floating_ip", network_api_release) - self.stubs.Set(network.api.API, "associate_floating_ip", - network_api_associate) + self.stubs.Set(compute.api.API, "associate_floating_ip", + compute_api_associate) self.stubs.Set(network.api.API, "disassociate_floating_ip", network_api_disassociate) self.context = context.get_admin_context() @@ -112,7 +120,7 @@ class FloatingIpTest(test.TestCase): self.assertTrue('floating_ip' in view) def test_floating_ips_list(self): - req = webob.Request.blank('/v1.1/os-floating-ips') + req = webob.Request.blank('/v1.1/123/os-floating-ips') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) res_dict = json.loads(res.body) @@ -127,65 +135,91 @@ class FloatingIpTest(test.TestCase): self.assertEqual(res_dict, response) def test_floating_ip_show(self): - req = webob.Request.blank('/v1.1/os-floating-ips/1') + req = webob.Request.blank('/v1.1/123/os-floating-ips/1') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) res_dict = json.loads(res.body) self.assertEqual(res_dict['floating_ip']['id'], 1) self.assertEqual(res_dict['floating_ip']['ip'], '10.10.10.10') - self.assertEqual(res_dict['floating_ip']['fixed_ip'], '11.0.0.1') self.assertEqual(res_dict['floating_ip']['instance_id'], None) def test_floating_ip_allocate(self): - req = webob.Request.blank('/v1.1/os-floating-ips') + req = webob.Request.blank('/v1.1/123/os-floating-ips') req.method = 'POST' req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app()) - print res self.assertEqual(res.status_int, 200) - ip = json.loads(res.body)['allocated'] + ip = json.loads(res.body)['floating_ip'] + expected = { "id": 1, - "floating_ip": '10.10.10.10'} + "instance_id": None, + "ip": "10.10.10.10", + "fixed_ip": None} self.assertEqual(ip, expected) def test_floating_ip_release(self): - req = webob.Request.blank('/v1.1/os-floating-ips/1') + req = webob.Request.blank('/v1.1/123/os-floating-ips/1') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - actual = json.loads(res.body)['released'] - expected = { - "id": 1, - "floating_ip": '10.10.10.10'} - self.assertEqual(actual, expected) + self.assertEqual(res.status_int, 202) - def test_floating_ip_associate(self): - body = dict(associate_address=dict(fixed_ip='1.2.3.4')) - req = webob.Request.blank('/v1.1/os-floating-ips/1/associate') - req.method = 'POST' + def test_add_floating_ip_to_instance(self): + body = dict(addFloatingIp=dict(address='11.0.0.1')) + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" req.body = json.dumps(body) req.headers["content-type"] = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - actual = json.loads(res.body)['associated'] - expected = { - "floating_ip_id": '1', - "floating_ip": "10.10.10.10", - "fixed_ip": "1.2.3.4"} - self.assertEqual(actual, expected) - - def test_floating_ip_disassociate(self): - body = dict() - req = webob.Request.blank('/v1.1/os-floating-ips/1/disassociate') - req.method = 'POST' + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 202) + + def test_remove_floating_ip_from_instance(self): + body = dict(removeFloatingIp=dict(address='11.0.0.1')) + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" req.body = json.dumps(body) - req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - ip = json.loads(res.body)['disassociated'] - expected = { - "floating_ip": '10.10.10.10', - "fixed_ip": '11.0.0.1'} - self.assertEqual(ip, expected) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 202) + + def test_bad_address_param_in_remove_floating_ip(self): + body = dict(removeFloatingIp=dict(badparam='11.0.0.1')) + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 400) + + def test_missing_dict_param_in_remove_floating_ip(self): + body = dict(removeFloatingIp='11.0.0.1') + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 400) + + def test_bad_address_param_in_add_floating_ip(self): + body = dict(addFloatingIp=dict(badparam='11.0.0.1')) + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 400) + + def test_missing_dict_param_in_add_floating_ip(self): + body = dict(addFloatingIp='11.0.0.1') + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 400) diff --git a/nova/tests/api/openstack/contrib/test_keypairs.py b/nova/tests/api/openstack/contrib/test_keypairs.py index eb3bc7af0..92e401aac 100644 --- a/nova/tests/api/openstack/contrib/test_keypairs.py +++ b/nova/tests/api/openstack/contrib/test_keypairs.py @@ -58,7 +58,7 @@ class KeypairsTest(test.TestCase): self.context = context.get_admin_context() def test_keypair_list(self): - req = webob.Request.blank('/v1.1/os-keypairs') + req = webob.Request.blank('/v1.1/123/os-keypairs') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) res_dict = json.loads(res.body) @@ -67,7 +67,7 @@ class KeypairsTest(test.TestCase): def test_keypair_create(self): body = {'keypair': {'name': 'create_test'}} - req = webob.Request.blank('/v1.1/os-keypairs') + req = webob.Request.blank('/v1.1/123/os-keypairs') req.method = 'POST' req.body = json.dumps(body) req.headers['Content-Type'] = 'application/json' @@ -93,7 +93,7 @@ class KeypairsTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/os-keypairs') + req = webob.Request.blank('/v1.1/123/os-keypairs') req.method = 'POST' req.body = json.dumps(body) req.headers['Content-Type'] = 'application/json' @@ -105,7 +105,7 @@ class KeypairsTest(test.TestCase): self.assertFalse('private_key' in res_dict['keypair']) def test_keypair_delete(self): - req = webob.Request.blank('/v1.1/os-keypairs/FAKE') + req = webob.Request.blank('/v1.1/123/os-keypairs/FAKE') req.method = 'DELETE' req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app()) diff --git a/nova/tests/api/openstack/contrib/test_multinic_xs.py b/nova/tests/api/openstack/contrib/test_multinic_xs.py index ac28f6be6..cecc4af4f 100644 --- a/nova/tests/api/openstack/contrib/test_multinic_xs.py +++ b/nova/tests/api/openstack/contrib/test_multinic_xs.py @@ -55,7 +55,7 @@ class FixedIpTest(test.TestCase): last_add_fixed_ip = (None, None) body = dict(addFixedIp=dict(networkId='test_net')) - req = webob.Request.blank('/v1.1/servers/test_inst/action') + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') req.method = 'POST' req.body = json.dumps(body) req.headers['content-type'] = 'application/json' @@ -69,7 +69,7 @@ class FixedIpTest(test.TestCase): last_add_fixed_ip = (None, None) body = dict(addFixedIp=dict()) - req = webob.Request.blank('/v1.1/servers/test_inst/action') + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') req.method = 'POST' req.body = json.dumps(body) req.headers['content-type'] = 'application/json' @@ -83,7 +83,7 @@ class FixedIpTest(test.TestCase): last_remove_fixed_ip = (None, None) body = dict(removeFixedIp=dict(address='10.10.10.1')) - req = webob.Request.blank('/v1.1/servers/test_inst/action') + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') req.method = 'POST' req.body = json.dumps(body) req.headers['content-type'] = 'application/json' @@ -97,7 +97,7 @@ class FixedIpTest(test.TestCase): last_remove_fixed_ip = (None, None) body = dict(removeFixedIp=dict()) - req = webob.Request.blank('/v1.1/servers/test_inst/action') + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') req.method = 'POST' req.body = json.dumps(body) req.headers['content-type'] = 'application/json' diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index f6a25385f..7faef08b2 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -78,7 +78,8 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(qs['injected_file_content_bytes'], 10240) def test_quotas_defaults(self): - req = webob.Request.blank('/v1.1/os-quota-sets/fake_tenant/defaults') + uri = '/v1.1/fake_tenant/os-quota-sets/fake_tenant/defaults' + req = webob.Request.blank(uri) req.method = 'GET' req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app()) @@ -99,7 +100,7 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(json.loads(res.body), expected) def test_quotas_show_as_admin(self): - req = webob.Request.blank('/v1.1/os-quota-sets/1234') + req = webob.Request.blank('/v1.1/1234/os-quota-sets/1234') req.method = 'GET' req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app( @@ -109,7 +110,7 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(json.loads(res.body), quota_set('1234')) def test_quotas_show_as_unauthorized_user(self): - req = webob.Request.blank('/v1.1/os-quota-sets/1234') + req = webob.Request.blank('/v1.1/fake/os-quota-sets/1234') req.method = 'GET' req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app( @@ -124,7 +125,7 @@ class QuotaSetsTest(test.TestCase): 'metadata_items': 128, 'injected_files': 5, 'injected_file_content_bytes': 10240}} - req = webob.Request.blank('/v1.1/os-quota-sets/update_me') + req = webob.Request.blank('/v1.1/1234/os-quota-sets/update_me') req.method = 'PUT' req.body = json.dumps(updated_quota_set) req.headers['Content-Type'] = 'application/json' @@ -141,7 +142,7 @@ class QuotaSetsTest(test.TestCase): 'metadata_items': 128, 'injected_files': 5, 'injected_file_content_bytes': 10240}} - req = webob.Request.blank('/v1.1/os-quota-sets/update_me') + req = webob.Request.blank('/v1.1/1234/os-quota-sets/update_me') req.method = 'PUT' req.body = json.dumps(updated_quota_set) req.headers['Content-Type'] = 'application/json' diff --git a/nova/tests/api/openstack/contrib/test_rescue.py b/nova/tests/api/openstack/contrib/test_rescue.py index fc8e4be4e..f8126d461 100644 --- a/nova/tests/api/openstack/contrib/test_rescue.py +++ b/nova/tests/api/openstack/contrib/test_rescue.py @@ -36,7 +36,7 @@ class RescueTest(test.TestCase): def test_rescue(self): body = dict(rescue=None) - req = webob.Request.blank('/v1.1/servers/test_inst/action') + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') req.method = "POST" req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -46,7 +46,7 @@ class RescueTest(test.TestCase): def test_unrescue(self): body = dict(unrescue=None) - req = webob.Request.blank('/v1.1/servers/test_inst/action') + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') req.method = "POST" req.body = json.dumps(body) req.headers["content-type"] = "application/json" diff --git a/nova/tests/api/openstack/contrib/test_security_groups.py b/nova/tests/api/openstack/contrib/test_security_groups.py index 4317880ca..bc1536911 100644 --- a/nova/tests/api/openstack/contrib/test_security_groups.py +++ b/nova/tests/api/openstack/contrib/test_security_groups.py @@ -15,17 +15,20 @@ # under the License. import json +import mox +import nova import unittest import webob from xml.dom import minidom +from nova import exception from nova import test from nova.api.openstack.contrib import security_groups from nova.tests.api.openstack import fakes def _get_create_request_json(body_dict): - req = webob.Request.blank('/v1.1/os-security-groups') + req = webob.Request.blank('/v1.1/123/os-security-groups') req.headers['Content-Type'] = 'application/json' req.method = 'POST' req.body = json.dumps(body_dict) @@ -51,6 +54,28 @@ def _create_security_group_request_dict(security_group): return {'security_group': sg} +def return_server(context, server_id): + return {'id': server_id, 'state': 0x01, 'host': "localhost"} + + +def return_non_running_server(context, server_id): + return {'id': server_id, 'state': 0x02, + 'host': "localhost"} + + +def return_security_group(context, project_id, group_name): + return {'id': 1, 'name': group_name, "instances": [ + {'id': 1}]} + + +def return_security_group_without_instances(context, project_id, group_name): + return {'id': 1, 'name': group_name} + + +def return_server_nonexistant(context, server_id): + raise exception.InstanceNotFound(instance_id=server_id) + + class TestSecurityGroups(test.TestCase): def setUp(self): super(TestSecurityGroups, self).setUp() @@ -84,7 +109,7 @@ class TestSecurityGroups(test.TestCase): return ''.join(body_parts) def _get_create_request_xml(self, body_dict): - req = webob.Request.blank('/v1.1/os-security-groups') + req = webob.Request.blank('/v1.1/123/os-security-groups') req.headers['Content-Type'] = 'application/xml' req.content_type = 'application/xml' req.accept = 'application/xml' @@ -99,7 +124,7 @@ class TestSecurityGroups(test.TestCase): return response def _delete_security_group(self, id): - request = webob.Request.blank('/v1.1/os-security-groups/%s' + request = webob.Request.blank('/v1.1/123/os-security-groups/%s' % id) request.method = 'DELETE' response = request.get_response(fakes.wsgi_app()) @@ -238,7 +263,7 @@ class TestSecurityGroups(test.TestCase): security_group['description'] = "group-description" response = _create_security_group_json(security_group) - req = webob.Request.blank('/v1.1/os-security-groups') + req = webob.Request.blank('/v1.1/123/os-security-groups') req.headers['Content-Type'] = 'application/json' req.method = 'GET' response = req.get_response(fakes.wsgi_app()) @@ -247,7 +272,7 @@ class TestSecurityGroups(test.TestCase): expected = {'security_groups': [ {'id': 1, 'name':"default", - 'tenant_id': "fake", + 'tenant_id': "123", "description":"default", "rules": [] }, @@ -257,7 +282,7 @@ class TestSecurityGroups(test.TestCase): { 'id': 2, 'name': "test", - 'tenant_id': "fake", + 'tenant_id': "123", "description": "group-description", "rules": [] } @@ -272,7 +297,7 @@ class TestSecurityGroups(test.TestCase): response = _create_security_group_json(security_group) res_dict = json.loads(response.body) - req = webob.Request.blank('/v1.1/os-security-groups/%s' % + req = webob.Request.blank('/v1.1/123/os-security-groups/%s' % res_dict['security_group']['id']) req.headers['Content-Type'] = 'application/json' req.method = 'GET' @@ -283,23 +308,22 @@ class TestSecurityGroups(test.TestCase): 'security_group': { 'id': 2, 'name': "test", - 'tenant_id': "fake", + 'tenant_id': "123", 'description': "group-description", 'rules': [] } } - self.assertEquals(response.status_int, 200) self.assertEquals(res_dict, expected) def test_get_security_group_by_invalid_id(self): - req = webob.Request.blank('/v1.1/os-security-groups/invalid') + req = webob.Request.blank('/v1.1/123/os-security-groups/invalid') req.headers['Content-Type'] = 'application/json' req.method = 'GET' response = req.get_response(fakes.wsgi_app()) self.assertEquals(response.status_int, 400) def test_get_security_group_by_non_existing_id(self): - req = webob.Request.blank('/v1.1/os-security-groups/111111111') + req = webob.Request.blank('/v1.1/123/os-security-groups/111111111') req.headers['Content-Type'] = 'application/json' req.method = 'GET' response = req.get_response(fakes.wsgi_app()) @@ -325,6 +349,252 @@ class TestSecurityGroups(test.TestCase): response = self._delete_security_group(11111111) self.assertEquals(response.status_int, 404) + def test_associate_by_non_existing_security_group_name(self): + body = dict(addSecurityGroup=dict(name='non-existing')) + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 404) + + def test_associate_by_invalid_server_id(self): + body = dict(addSecurityGroup=dict(name='test')) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group) + req = webob.Request.blank('/v1.1/123/servers/invalid/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_associate_without_body(self): + req = webob.Request.blank('/v1.1/123/servers/1/action') + body = dict(addSecurityGroup=None) + self.stubs.Set(nova.db, 'instance_get', return_server) + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_associate_no_security_group_name(self): + req = webob.Request.blank('/v1.1/123/servers/1/action') + body = dict(addSecurityGroup=dict()) + self.stubs.Set(nova.db, 'instance_get', return_server) + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_associate_security_group_name_with_whitespaces(self): + req = webob.Request.blank('/v1.1/123/servers/1/action') + body = dict(addSecurityGroup=dict(name=" ")) + self.stubs.Set(nova.db, 'instance_get', return_server) + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_associate_non_existing_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) + body = dict(addSecurityGroup=dict(name="test")) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group) + req = webob.Request.blank('/v1.1/123/servers/10000/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 404) + + def test_associate_non_running_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_non_running_server) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_without_instances) + body = dict(addSecurityGroup=dict(name="test")) + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_associate_already_associated_security_group_to_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group) + body = dict(addSecurityGroup=dict(name="test")) + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_associate(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.mox.StubOutWithMock(nova.db, 'instance_add_security_group') + nova.db.instance_add_security_group(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_without_instances) + self.mox.ReplayAll() + + body = dict(addSecurityGroup=dict(name="test")) + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 202) + + def test_associate_xml(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.mox.StubOutWithMock(nova.db, 'instance_add_security_group') + nova.db.instance_add_security_group(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_without_instances) + self.mox.ReplayAll() + + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/xml' + req.method = 'POST' + req.body = """<addSecurityGroup> + <name>test</name> + </addSecurityGroup>""" + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 202) + + def test_disassociate_by_non_existing_security_group_name(self): + body = dict(removeSecurityGroup=dict(name='non-existing')) + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 404) + + def test_disassociate_by_invalid_server_id(self): + body = dict(removeSecurityGroup=dict(name='test')) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group) + req = webob.Request.blank('/v1.1/123/servers/invalid/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_disassociate_without_body(self): + req = webob.Request.blank('/v1.1/123/servers/1/action') + body = dict(removeSecurityGroup=None) + self.stubs.Set(nova.db, 'instance_get', return_server) + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_disassociate_no_security_group_name(self): + req = webob.Request.blank('/v1.1/123/servers/1/action') + body = dict(removeSecurityGroup=dict()) + self.stubs.Set(nova.db, 'instance_get', return_server) + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_disassociate_security_group_name_with_whitespaces(self): + req = webob.Request.blank('/v1.1/123/servers/1/action') + body = dict(removeSecurityGroup=dict(name=" ")) + self.stubs.Set(nova.db, 'instance_get', return_server) + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_disassociate_non_existing_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) + body = dict(removeSecurityGroup=dict(name="test")) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group) + req = webob.Request.blank('/v1.1/123/servers/10000/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 404) + + def test_disassociate_non_running_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_non_running_server) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group) + body = dict(removeSecurityGroup=dict(name="test")) + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_disassociate_already_associated_security_group_to_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_without_instances) + body = dict(removeSecurityGroup=dict(name="test")) + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 400) + + def test_disassociate(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.mox.StubOutWithMock(nova.db, 'instance_remove_security_group') + nova.db.instance_remove_security_group(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group) + self.mox.ReplayAll() + + body = dict(removeSecurityGroup=dict(name="test")) + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body) + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 202) + + def test_disassociate_xml(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.mox.StubOutWithMock(nova.db, 'instance_remove_security_group') + nova.db.instance_remove_security_group(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group) + self.mox.ReplayAll() + + req = webob.Request.blank('/v1.1/123/servers/1/action') + req.headers['Content-Type'] = 'application/xml' + req.method = 'POST' + req.body = """<removeSecurityGroup> + <name>test</name> + </removeSecurityGroup>""" + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 202) + class TestSecurityGroupRules(test.TestCase): def setUp(self): @@ -354,7 +624,7 @@ class TestSecurityGroupRules(test.TestCase): super(TestSecurityGroupRules, self).tearDown() def _create_security_group_rule_json(self, rules): - request = webob.Request.blank('/v1.1/os-security-group-rules') + request = webob.Request.blank('/v1.1/123/os-security-group-rules') request.headers['Content-Type'] = 'application/json' request.method = 'POST' request.body = json.dumps(rules) @@ -362,7 +632,7 @@ class TestSecurityGroupRules(test.TestCase): return response def _delete_security_group_rule(self, id): - request = webob.Request.blank('/v1.1/os-security-group-rules/%s' + request = webob.Request.blank('/v1.1/123/os-security-group-rules/%s' % id) request.method = 'DELETE' response = request.get_response(fakes.wsgi_app()) @@ -420,7 +690,7 @@ class TestSecurityGroupRules(test.TestCase): self.assertEquals(response.status_int, 400) def test_create_with_no_body_json(self): - request = webob.Request.blank('/v1.1/os-security-group-rules') + request = webob.Request.blank('/v1.1/123/os-security-group-rules') request.headers['Content-Type'] = 'application/json' request.method = 'POST' request.body = json.dumps(None) @@ -428,7 +698,7 @@ class TestSecurityGroupRules(test.TestCase): self.assertEquals(response.status_int, 422) def test_create_with_no_security_group_rule_in_body_json(self): - request = webob.Request.blank('/v1.1/os-security-group-rules') + request = webob.Request.blank('/v1.1/123/os-security-group-rules') request.headers['Content-Type'] = 'application/json' request.method = 'POST' body_dict = {'test': "test"} diff --git a/nova/tests/api/openstack/contrib/test_virtual_interfaces.py b/nova/tests/api/openstack/contrib/test_virtual_interfaces.py index d541a9e95..1db253b35 100644 --- a/nova/tests/api/openstack/contrib/test_virtual_interfaces.py +++ b/nova/tests/api/openstack/contrib/test_virtual_interfaces.py @@ -43,7 +43,7 @@ class ServerVirtualInterfaceTest(test.TestCase): super(ServerVirtualInterfaceTest, self).tearDown() def test_get_virtual_interfaces_list(self): - req = webob.Request.blank('/v1.1/servers/1/os-virtual-interfaces') + req = webob.Request.blank('/v1.1/123/servers/1/os-virtual-interfaces') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) res_dict = json.loads(res.body) diff --git a/nova/tests/api/openstack/extensions/foxinsocks.py b/nova/tests/api/openstack/extensions/foxinsocks.py index 03aad007a..2d8313cf6 100644 --- a/nova/tests/api/openstack/extensions/foxinsocks.py +++ b/nova/tests/api/openstack/extensions/foxinsocks.py @@ -72,8 +72,9 @@ class Foxinsocks(object): res.body = json.dumps(data) return res - req_ext1 = extensions.RequestExtension('GET', '/v1.1/flavors/:(id)', - _goose_handler) + req_ext1 = extensions.RequestExtension('GET', + '/v1.1/:(project_id)/flavors/:(id)', + _goose_handler) request_exts.append(req_ext1) def _bands_handler(req, res): @@ -84,8 +85,9 @@ class Foxinsocks(object): res.body = json.dumps(data) return res - req_ext2 = extensions.RequestExtension('GET', '/v1.1/flavors/:(id)', - _bands_handler) + req_ext2 = extensions.RequestExtension('GET', + '/v1.1/:(project_id)/flavors/:(id)', + _bands_handler) request_exts.append(req_ext2) return request_exts diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index 306ae1aa0..7fd3935a2 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -53,6 +53,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'user1' req.headers['X-Auth-Key'] = 'user1_key' + req.headers['X-Auth-Project-Id'] = 'user1_project' result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '204 No Content') self.assertEqual(len(result.headers['X-Auth-Token']), 40) @@ -73,14 +74,14 @@ class Test(test.TestCase): self.assertEqual(result.status, '204 No Content') self.assertEqual(len(result.headers['X-Auth-Token']), 40) self.assertEqual(result.headers['X-Server-Management-Url'], - "http://foo/v1.0/") + "http://foo/v1.0") self.assertEqual(result.headers['X-CDN-Management-Url'], "") self.assertEqual(result.headers['X-Storage-Url'], "") token = result.headers['X-Auth-Token'] self.stubs.Set(nova.api.openstack, 'APIRouterV10', fakes.FakeRouter) - req = webob.Request.blank('/v1.0/fake') + req = webob.Request.blank('/v1.0/user1_project') req.headers['X-Auth-Token'] = token result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '200 OK') @@ -125,7 +126,7 @@ class Test(test.TestCase): token = result.headers['X-Auth-Token'] self.stubs.Set(nova.api.openstack, 'APIRouterV10', fakes.FakeRouter) - req = webob.Request.blank('/v1.0/fake') + req = webob.Request.blank('/v1.0/') req.headers['X-Auth-Token'] = token req.headers['X-Auth-Project-Id'] = 'user2_project' result = req.get_response(fakes.wsgi_app(fake_auth=False)) @@ -136,6 +137,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'unknown_user' req.headers['X-Auth-Key'] = 'unknown_user_key' + req.headers['X-Auth-Project-Id'] = 'user_project' result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index a25f4e13c..9f923852d 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -103,7 +103,7 @@ class ExtensionControllerTest(test.TestCase): def test_list_extensions_json(self): app = openstack.APIRouterV11() ext_midware = extensions.ExtensionMiddleware(app) - request = webob.Request.blank("/extensions") + request = webob.Request.blank("/123/extensions") response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) @@ -129,7 +129,7 @@ class ExtensionControllerTest(test.TestCase): def test_get_extension_json(self): app = openstack.APIRouterV11() ext_midware = extensions.ExtensionMiddleware(app) - request = webob.Request.blank("/extensions/FOXNSOX") + request = webob.Request.blank("/123/extensions/FOXNSOX") response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) @@ -145,7 +145,7 @@ class ExtensionControllerTest(test.TestCase): def test_list_extensions_xml(self): app = openstack.APIRouterV11() ext_midware = extensions.ExtensionMiddleware(app) - request = webob.Request.blank("/extensions") + request = webob.Request.blank("/123/extensions") request.accept = "application/xml" response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) @@ -172,7 +172,7 @@ class ExtensionControllerTest(test.TestCase): def test_get_extension_xml(self): app = openstack.APIRouterV11() ext_midware = extensions.ExtensionMiddleware(app) - request = webob.Request.blank("/extensions/FOXNSOX") + request = webob.Request.blank("/123/extensions/FOXNSOX") request.accept = "application/xml" response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) @@ -213,7 +213,7 @@ class ResourceExtensionTest(test.TestCase): manager = StubExtensionManager(res_ext) app = openstack.APIRouterV11() ext_midware = extensions.ExtensionMiddleware(app, manager) - request = webob.Request.blank("/tweedles") + request = webob.Request.blank("/123/tweedles") response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) self.assertEqual(response_body, response.body) @@ -224,7 +224,7 @@ class ResourceExtensionTest(test.TestCase): manager = StubExtensionManager(res_ext) app = openstack.APIRouterV11() ext_midware = extensions.ExtensionMiddleware(app, manager) - request = webob.Request.blank("/tweedles") + request = webob.Request.blank("/123/tweedles") response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) self.assertEqual(response_body, response.body) @@ -248,7 +248,7 @@ class ExtensionManagerTest(test.TestCase): def test_get_resources(self): app = openstack.APIRouterV11() ext_midware = extensions.ExtensionMiddleware(app) - request = webob.Request.blank("/foxnsocks") + request = webob.Request.blank("/123/foxnsocks") response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) self.assertEqual(response_body, response.body) @@ -281,23 +281,26 @@ class ActionExtensionTest(test.TestCase): def test_extended_action(self): body = dict(add_tweedle=dict(name="test")) - response = self._send_server_action_request("/servers/1/action", body) + url = "/123/servers/1/action" + response = self._send_server_action_request(url, body) self.assertEqual(200, response.status_int) self.assertEqual("Tweedle Beetle Added.", response.body) body = dict(delete_tweedle=dict(name="test")) - response = self._send_server_action_request("/servers/1/action", body) + response = self._send_server_action_request(url, body) self.assertEqual(200, response.status_int) self.assertEqual("Tweedle Beetle Deleted.", response.body) def test_invalid_action_body(self): body = dict(blah=dict(name="test")) # Doesn't exist - response = self._send_server_action_request("/servers/1/action", body) + url = "/123/servers/1/action" + response = self._send_server_action_request(url, body) self.assertEqual(400, response.status_int) def test_invalid_action(self): body = dict(blah=dict(name="test")) - response = self._send_server_action_request("/fdsa/1/action", body) + url = "/123/fdsa/1/action" + response = self._send_server_action_request(url, body) self.assertEqual(404, response.status_int) @@ -318,13 +321,13 @@ class RequestExtensionTest(test.TestCase): return res req_ext = extensions.RequestExtension('GET', - '/v1.1/flavors/:(id)', + '/v1.1/123/flavors/:(id)', _req_handler) manager = StubExtensionManager(None, None, req_ext) app = fakes.wsgi_app() ext_midware = extensions.ExtensionMiddleware(app, manager) - request = webob.Request.blank("/v1.1/flavors/1?chewing=bluegoo") + request = webob.Request.blank("/v1.1/123/flavors/1?chewing=bluegoo") request.environ['api.version'] = '1.1' response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) @@ -335,7 +338,7 @@ class RequestExtensionTest(test.TestCase): app = fakes.wsgi_app() ext_midware = extensions.ExtensionMiddleware(app) - request = webob.Request.blank("/v1.1/flavors/1?chewing=newblue") + request = webob.Request.blank("/v1.1/123/flavors/1?chewing=newblue") request.environ['api.version'] = '1.1' response = request.get_response(ext_midware) self.assertEqual(200, response.status_int) diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index d0fe72001..812bece42 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -138,7 +138,7 @@ class FlavorsTest(test.TestCase): self.assertEqual(res.status_int, 404) def test_get_flavor_by_id_v1_1(self): - req = webob.Request.blank('/v1.1/flavors/12') + req = webob.Request.blank('/v1.1/fake/flavors/12') req.environ['api.version'] = '1.1' res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) @@ -152,11 +152,11 @@ class FlavorsTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/12", + "href": "http://localhost/v1.1/fake/flavors/12", }, { "rel": "bookmark", - "href": "http://localhost/flavors/12", + "href": "http://localhost/fake/flavors/12", }, ], }, @@ -164,7 +164,7 @@ class FlavorsTest(test.TestCase): self.assertEqual(flavor, expected) def test_get_flavor_list_v1_1(self): - req = webob.Request.blank('/v1.1/flavors') + req = webob.Request.blank('/v1.1/fake/flavors') req.environ['api.version'] = '1.1' res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) @@ -177,11 +177,11 @@ class FlavorsTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/1", + "href": "http://localhost/v1.1/fake/flavors/1", }, { "rel": "bookmark", - "href": "http://localhost/flavors/1", + "href": "http://localhost/fake/flavors/1", }, ], }, @@ -191,11 +191,11 @@ class FlavorsTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/2", + "href": "http://localhost/v1.1/fake/flavors/2", }, { "rel": "bookmark", - "href": "http://localhost/flavors/2", + "href": "http://localhost/fake/flavors/2", }, ], }, @@ -204,7 +204,7 @@ class FlavorsTest(test.TestCase): self.assertEqual(flavor, expected) def test_get_flavor_list_detail_v1_1(self): - req = webob.Request.blank('/v1.1/flavors/detail') + req = webob.Request.blank('/v1.1/fake/flavors/detail') req.environ['api.version'] = '1.1' res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) @@ -219,11 +219,11 @@ class FlavorsTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/1", + "href": "http://localhost/v1.1/fake/flavors/1", }, { "rel": "bookmark", - "href": "http://localhost/flavors/1", + "href": "http://localhost/fake/flavors/1", }, ], }, @@ -235,11 +235,11 @@ class FlavorsTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/2", + "href": "http://localhost/v1.1/fake/flavors/2", }, { "rel": "bookmark", - "href": "http://localhost/flavors/2", + "href": "http://localhost/fake/flavors/2", }, ], }, @@ -252,7 +252,7 @@ class FlavorsTest(test.TestCase): return {} self.stubs.Set(nova.db.api, "instance_type_get_all", _return_empty) - req = webob.Request.blank('/v1.1/flavors') + req = webob.Request.blank('/v1.1/fake/flavors') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) flavors = json.loads(res.body)["flavors"] @@ -274,11 +274,11 @@ class FlavorsXMLSerializationTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/12", + "href": "http://localhost/v1.1/fake/flavors/12", }, { "rel": "bookmark", - "href": "http://localhost/flavors/12", + "href": "http://localhost/fake/flavors/12", }, ], }, @@ -294,8 +294,10 @@ class FlavorsXMLSerializationTest(test.TestCase): name="asdf" ram="256" disk="10"> - <atom:link href="http://localhost/v1.1/flavors/12" rel="self"/> - <atom:link href="http://localhost/flavors/12" rel="bookmark"/> + <atom:link href="http://localhost/v1.1/fake/flavors/12" + rel="self"/> + <atom:link href="http://localhost/fake/flavors/12" + rel="bookmark"/> </flavor> """.replace(" ", "")) @@ -313,11 +315,11 @@ class FlavorsXMLSerializationTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/12", + "href": "http://localhost/v1.1/fake/flavors/12", }, { "rel": "bookmark", - "href": "http://localhost/flavors/12", + "href": "http://localhost/fake/flavors/12", }, ], }, @@ -333,8 +335,10 @@ class FlavorsXMLSerializationTest(test.TestCase): name="asdf" ram="256" disk="10"> - <atom:link href="http://localhost/v1.1/flavors/12" rel="self"/> - <atom:link href="http://localhost/flavors/12" rel="bookmark"/> + <atom:link href="http://localhost/v1.1/fake/flavors/12" + rel="self"/> + <atom:link href="http://localhost/fake/flavors/12" + rel="bookmark"/> </flavor> """.replace(" ", "")) @@ -353,11 +357,11 @@ class FlavorsXMLSerializationTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/23", + "href": "http://localhost/v1.1/fake/flavors/23", }, { "rel": "bookmark", - "href": "http://localhost/flavors/23", + "href": "http://localhost/fake/flavors/23", }, ], }, { @@ -368,11 +372,11 @@ class FlavorsXMLSerializationTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/13", + "href": "http://localhost/v1.1/fake/flavors/13", }, { "rel": "bookmark", - "href": "http://localhost/flavors/13", + "href": "http://localhost/fake/flavors/13", }, ], }, @@ -389,15 +393,19 @@ class FlavorsXMLSerializationTest(test.TestCase): name="flavor 23" ram="512" disk="20"> - <atom:link href="http://localhost/v1.1/flavors/23" rel="self"/> - <atom:link href="http://localhost/flavors/23" rel="bookmark"/> + <atom:link href="http://localhost/v1.1/fake/flavors/23" + rel="self"/> + <atom:link href="http://localhost/fake/flavors/23" + rel="bookmark"/> </flavor> <flavor id="13" name="flavor 13" ram="256" disk="10"> - <atom:link href="http://localhost/v1.1/flavors/13" rel="self"/> - <atom:link href="http://localhost/flavors/13" rel="bookmark"/> + <atom:link href="http://localhost/v1.1/fake/flavors/13" + rel="self"/> + <atom:link href="http://localhost/fake/flavors/13" + rel="bookmark"/> </flavor> </flavors> """.replace(" ", "") % locals()) @@ -417,11 +425,11 @@ class FlavorsXMLSerializationTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/23", + "href": "http://localhost/v1.1/fake/flavors/23", }, { "rel": "bookmark", - "href": "http://localhost/flavors/23", + "href": "http://localhost/fake/flavors/23", }, ], }, { @@ -432,11 +440,11 @@ class FlavorsXMLSerializationTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/flavors/13", + "href": "http://localhost/v1.1/fake/flavors/13", }, { "rel": "bookmark", - "href": "http://localhost/flavors/13", + "href": "http://localhost/fake/flavors/13", }, ], }, @@ -450,12 +458,16 @@ class FlavorsXMLSerializationTest(test.TestCase): <flavors xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom"> <flavor id="23" name="flavor 23"> - <atom:link href="http://localhost/v1.1/flavors/23" rel="self"/> - <atom:link href="http://localhost/flavors/23" rel="bookmark"/> + <atom:link href="http://localhost/v1.1/fake/flavors/23" + rel="self"/> + <atom:link href="http://localhost/fake/flavors/23" + rel="bookmark"/> </flavor> <flavor id="13" name="flavor 13"> - <atom:link href="http://localhost/v1.1/flavors/13" rel="self"/> - <atom:link href="http://localhost/flavors/13" rel="bookmark"/> + <atom:link href="http://localhost/v1.1/fake/flavors/13" + rel="self"/> + <atom:link href="http://localhost/fake/flavors/13" + rel="bookmark"/> </flavor> </flavors> """.replace(" ", "") % locals()) diff --git a/nova/tests/api/openstack/test_flavors_extra_specs.py b/nova/tests/api/openstack/test_flavors_extra_specs.py index ccd1b0d9f..f382d06e9 100644 --- a/nova/tests/api/openstack/test_flavors_extra_specs.py +++ b/nova/tests/api/openstack/test_flavors_extra_specs.py @@ -63,7 +63,7 @@ class FlavorsExtraSpecsTest(test.TestCase): def test_index(self): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', return_flavor_extra_specs) - request = webob.Request.blank('/v1.1/flavors/1/os-extra_specs') + request = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs') res = request.get_response(fakes.wsgi_app()) self.assertEqual(200, res.status_int) res_dict = json.loads(res.body) @@ -73,7 +73,7 @@ class FlavorsExtraSpecsTest(test.TestCase): def test_index_no_data(self): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', return_empty_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(200, res.status_int) @@ -83,7 +83,7 @@ class FlavorsExtraSpecsTest(test.TestCase): def test_show(self): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', return_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key5') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs/key5') res = req.get_response(fakes.wsgi_app()) self.assertEqual(200, res.status_int) res_dict = json.loads(res.body) @@ -93,7 +93,7 @@ class FlavorsExtraSpecsTest(test.TestCase): def test_show_spec_not_found(self): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', return_empty_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key6') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs/key6') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(404, res.status_int) @@ -101,7 +101,7 @@ class FlavorsExtraSpecsTest(test.TestCase): def test_delete(self): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_delete', delete_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key5') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs/key5') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) self.assertEqual(200, res.status_int) @@ -110,7 +110,7 @@ class FlavorsExtraSpecsTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_update_or_create', return_create_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs') req.method = 'POST' req.body = '{"extra_specs": {"key1": "value1"}}' req.headers["content-type"] = "application/json" @@ -124,7 +124,7 @@ class FlavorsExtraSpecsTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_update_or_create', return_create_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs') req.method = 'POST' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) @@ -134,7 +134,7 @@ class FlavorsExtraSpecsTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_update_or_create', return_create_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs/key1') req.method = 'PUT' req.body = '{"key1": "value1"}' req.headers["content-type"] = "application/json" @@ -148,7 +148,7 @@ class FlavorsExtraSpecsTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_update_or_create', return_create_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs/key1') req.method = 'PUT' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) @@ -158,7 +158,7 @@ class FlavorsExtraSpecsTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_update_or_create', return_create_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs/key1') req.method = 'PUT' req.body = '{"key1": "value1", "key2": "value2"}' req.headers["content-type"] = "application/json" @@ -169,7 +169,7 @@ class FlavorsExtraSpecsTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_type_extra_specs_update_or_create', return_create_flavor_extra_specs) - req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/bad') + req = webob.Request.blank('/v1.1/123/flavors/1/os-extra_specs/bad') req.method = 'PUT' req.body = '{"key1": "value1"}' req.headers["content-type"] = "application/json" diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index 21743eeef..fe42e35e5 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -90,7 +90,7 @@ class ImageMetaDataTest(test.TestCase): fakes.stub_out_glance(self.stubs, self.IMAGE_FIXTURES) def test_index(self): - req = webob.Request.blank('/v1.1/images/1/metadata') + req = webob.Request.blank('/v1.1/123/images/1/metadata') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(200, res.status_int) @@ -100,7 +100,7 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(value, res_dict['metadata'][key]) def test_show(self): - req = webob.Request.blank('/v1.1/images/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/images/1/metadata/key1') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(200, res.status_int) @@ -109,12 +109,12 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual('value1', res_dict['meta']['key1']) def test_show_not_found(self): - req = webob.Request.blank('/v1.1/images/1/metadata/key9') + req = webob.Request.blank('/v1.1/fake/images/1/metadata/key9') res = req.get_response(fakes.wsgi_app()) self.assertEqual(404, res.status_int) def test_create(self): - req = webob.Request.blank('/v1.1/images/2/metadata') + req = webob.Request.blank('/v1.1/fake/images/2/metadata') req.method = 'POST' req.body = '{"metadata": {"key9": "value9"}}' req.headers["content-type"] = "application/json" @@ -134,7 +134,7 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(expected_output, actual_output) def test_update_all(self): - req = webob.Request.blank('/v1.1/images/2/metadata') + req = webob.Request.blank('/v1.1/fake/images/1/metadata') req.method = 'PUT' req.body = '{"metadata": {"key9": "value9"}}' req.headers["content-type"] = "application/json" @@ -152,7 +152,7 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(expected_output, actual_output) def test_update_item(self): - req = webob.Request.blank('/v1.1/images/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/images/1/metadata/key1') req.method = 'PUT' req.body = '{"meta": {"key1": "zz"}}' req.headers["content-type"] = "application/json" @@ -168,7 +168,7 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(actual_output, expected_output) def test_update_item_bad_body(self): - req = webob.Request.blank('/v1.1/images/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/images/1/metadata/key1') req.method = 'PUT' req.body = '{"key1": "zz"}' req.headers["content-type"] = "application/json" @@ -176,7 +176,7 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(400, res.status_int) def test_update_item_too_many_keys(self): - req = webob.Request.blank('/v1.1/images/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/images/1/metadata/key1') req.method = 'PUT' req.body = '{"meta": {"key1": "value1", "key2": "value2"}}' req.headers["content-type"] = "application/json" @@ -184,7 +184,7 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(400, res.status_int) def test_update_item_body_uri_mismatch(self): - req = webob.Request.blank('/v1.1/images/1/metadata/bad') + req = webob.Request.blank('/v1.1/fake/images/1/metadata/bad') req.method = 'PUT' req.body = '{"meta": {"key1": "value1"}}' req.headers["content-type"] = "application/json" @@ -192,7 +192,7 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(400, res.status_int) def test_update_item_xml(self): - req = webob.Request.blank('/v1.1/images/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/images/1/metadata/key1') req.method = 'PUT' req.body = '<meta key="key1">five</meta>' req.headers["content-type"] = "application/xml" @@ -208,14 +208,14 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(actual_output, expected_output) def test_delete(self): - req = webob.Request.blank('/v1.1/images/2/metadata/key1') + req = webob.Request.blank('/v1.1/fake/images/2/metadata/key1') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) self.assertEqual(204, res.status_int) self.assertEqual('', res.body) def test_delete_not_found(self): - req = webob.Request.blank('/v1.1/images/2/metadata/blah') + req = webob.Request.blank('/v1.1/fake/images/2/metadata/blah') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) self.assertEqual(404, res.status_int) @@ -225,7 +225,7 @@ class ImageMetaDataTest(test.TestCase): for num in range(FLAGS.quota_metadata_items + 1): data['metadata']['key%i' % num] = "blah" json_string = str(data).replace("\'", "\"") - req = webob.Request.blank('/v1.1/images/2/metadata') + req = webob.Request.blank('/v1.1/fake/images/2/metadata') req.method = 'POST' req.body = json_string req.headers["content-type"] = "application/json" @@ -233,7 +233,7 @@ class ImageMetaDataTest(test.TestCase): self.assertEqual(413, res.status_int) def test_too_many_metadata_items_on_put(self): - req = webob.Request.blank('/v1.1/images/3/metadata/blah') + req = webob.Request.blank('/v1.1/fake/images/3/metadata/blah') req.method = 'PUT' req.body = '{"meta": {"blah": "blah"}}' req.headers["content-type"] = "application/json" diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 383ed2e03..2a7cfc382 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -339,6 +339,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.stubs.UnsetAll() super(ImageControllerWithGlanceServiceTest, self).tearDown() + def _get_fake_context(self): + class Context(object): + project_id = 'fake' + return Context() + def _applicable_fixture(self, fixture, user_id): """Determine if this fixture is applicable for given user id.""" is_public = fixture["is_public"] @@ -386,13 +391,13 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertEqual(expected_image, actual_image) def test_get_image_v1_1(self): - request = webob.Request.blank('/v1.1/images/124') + request = webob.Request.blank('/v1.1/fake/images/124') response = request.get_response(fakes.wsgi_app()) actual_image = json.loads(response.body) - href = "http://localhost/v1.1/images/124" - bookmark = "http://localhost/images/124" + href = "http://localhost/v1.1/fake/images/124" + bookmark = "http://localhost/fake/images/124" server_href = "http://localhost/v1.1/servers/42" server_bookmark = "http://localhost/servers/42" @@ -508,7 +513,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertEqual(expected.toxml(), actual.toxml()) def test_get_image_404_v1_1_json(self): - request = webob.Request.blank('/v1.1/images/NonExistantImage') + request = webob.Request.blank('/v1.1/fake/images/NonExistantImage') response = request.get_response(fakes.wsgi_app()) self.assertEqual(404, response.status_int) @@ -524,7 +529,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertEqual(expected, actual) def test_get_image_404_v1_1_xml(self): - request = webob.Request.blank('/v1.1/images/NonExistantImage') + request = webob.Request.blank('/v1.1/fake/images/NonExistantImage') request.accept = "application/xml" response = request.get_response(fakes.wsgi_app()) self.assertEqual(404, response.status_int) @@ -545,7 +550,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertEqual(expected.toxml(), actual.toxml()) def test_get_image_index_v1_1(self): - request = webob.Request.blank('/v1.1/images') + request = webob.Request.blank('/v1.1/fake/images') response = request.get_response(fakes.wsgi_app()) response_dict = json.loads(response.body) @@ -558,8 +563,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): fixtures.remove(image) continue - href = "http://localhost/v1.1/images/%s" % image["id"] - bookmark = "http://localhost/images/%s" % image["id"] + href = "http://localhost/v1.1/fake/images/%s" % image["id"] + bookmark = "http://localhost/fake/images/%s" % image["id"] test_image = { "id": image["id"], "name": image["name"], @@ -637,7 +642,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertDictListMatch(expected, response_list) def test_get_image_details_v1_1(self): - request = webob.Request.blank('/v1.1/images/detail') + request = webob.Request.blank('/v1.1/fake/images/detail') response = request.get_response(fakes.wsgi_app()) response_dict = json.loads(response.body) @@ -655,11 +660,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'progress': 100, "links": [{ "rel": "self", - "href": "http://localhost/v1.1/images/123", + "href": "http://localhost/v1.1/fake/images/123", }, { "rel": "bookmark", - "href": "http://localhost/images/123", + "href": "http://localhost/fake/images/123", }], }, { @@ -686,11 +691,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }, "links": [{ "rel": "self", - "href": "http://localhost/v1.1/images/124", + "href": "http://localhost/v1.1/fake/images/124", }, { "rel": "bookmark", - "href": "http://localhost/images/124", + "href": "http://localhost/fake/images/124", }], }, { @@ -717,11 +722,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }, "links": [{ "rel": "self", - "href": "http://localhost/v1.1/images/125", + "href": "http://localhost/v1.1/fake/images/125", }, { "rel": "bookmark", - "href": "http://localhost/images/125", + "href": "http://localhost/fake/images/125", }], }, { @@ -748,11 +753,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }, "links": [{ "rel": "self", - "href": "http://localhost/v1.1/images/126", + "href": "http://localhost/v1.1/fake/images/126", }, { "rel": "bookmark", - "href": "http://localhost/images/126", + "href": "http://localhost/fake/images/126", }], }, { @@ -779,11 +784,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }, "links": [{ "rel": "self", - "href": "http://localhost/v1.1/images/127", + "href": "http://localhost/v1.1/fake/images/127", }, { "rel": "bookmark", - "href": "http://localhost/images/127", + "href": "http://localhost/fake/images/127", }], }, { @@ -796,11 +801,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'progress': 100, "links": [{ "rel": "self", - "href": "http://localhost/v1.1/images/129", + "href": "http://localhost/v1.1/fake/images/129", }, { "rel": "bookmark", - "href": "http://localhost/images/129", + "href": "http://localhost/fake/images/129", }], }, ] @@ -809,7 +814,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_filter_with_name(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'name': 'testname'} image_service.index(context, filters=filters).AndReturn([]) self.mox.ReplayAll() @@ -821,7 +826,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_filter_with_status(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'status': 'ACTIVE'} image_service.index(context, filters=filters).AndReturn([]) self.mox.ReplayAll() @@ -833,7 +838,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_filter_with_property(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'property-test': '3'} image_service.index(context, filters=filters).AndReturn([]) self.mox.ReplayAll() @@ -845,7 +850,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_filter_server(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() # 'server' should be converted to 'property-instance_ref' filters = {'property-instance_ref': 'http://localhost:8774/servers/12'} image_service.index(context, filters=filters).AndReturn([]) @@ -859,7 +864,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_filter_changes_since(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'changes-since': '2011-01-24T17:08Z'} image_service.index(context, filters=filters).AndReturn([]) self.mox.ReplayAll() @@ -872,7 +877,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_filter_with_type(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'property-image_type': 'BASE'} image_service.index(context, filters=filters).AndReturn([]) self.mox.ReplayAll() @@ -884,7 +889,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_filter_not_supported(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'status': 'ACTIVE'} image_service.detail(context, filters=filters).AndReturn([]) self.mox.ReplayAll() @@ -897,7 +902,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_no_filters(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {} image_service.index( context, filters=filters).AndReturn([]) @@ -911,11 +916,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_detail_filter_with_name(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'name': 'testname'} image_service.detail(context, filters=filters).AndReturn([]) self.mox.ReplayAll() - request = webob.Request.blank('/v1.1/images/detail?name=testname') + request = webob.Request.blank('/v1.1/fake/images/detail?name=testname') request.environ['nova.context'] = context controller = images.ControllerV11(image_service=image_service) controller.detail(request) @@ -923,11 +928,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_detail_filter_with_status(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'status': 'ACTIVE'} image_service.detail(context, filters=filters).AndReturn([]) self.mox.ReplayAll() - request = webob.Request.blank('/v1.1/images/detail?status=ACTIVE') + request = webob.Request.blank('/v1.1/fake/images/detail?status=ACTIVE') request.environ['nova.context'] = context controller = images.ControllerV11(image_service=image_service) controller.detail(request) @@ -935,11 +940,12 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_detail_filter_with_property(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'property-test': '3'} image_service.detail(context, filters=filters).AndReturn([]) self.mox.ReplayAll() - request = webob.Request.blank('/v1.1/images/detail?property-test=3') + request = webob.Request.blank( + '/v1.1/fake/images/detail?property-test=3') request.environ['nova.context'] = context controller = images.ControllerV11(image_service=image_service) controller.detail(request) @@ -947,12 +953,12 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_detail_filter_server(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() # 'server' should be converted to 'property-instance_ref' filters = {'property-instance_ref': 'http://localhost:8774/servers/12'} image_service.index(context, filters=filters).AndReturn([]) self.mox.ReplayAll() - request = webob.Request.blank('/v1.1/images/detail?server=' + request = webob.Request.blank('/v1.1/fake/images/detail?server=' 'http://localhost:8774/servers/12') request.environ['nova.context'] = context controller = images.ControllerV11(image_service=image_service) @@ -961,11 +967,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_detail_filter_changes_since(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'changes-since': '2011-01-24T17:08Z'} image_service.index(context, filters=filters).AndReturn([]) self.mox.ReplayAll() - request = webob.Request.blank('/v1.1/images/detail?changes-since=' + request = webob.Request.blank('/v1.1/fake/images/detail?changes-since=' '2011-01-24T17:08Z') request.environ['nova.context'] = context controller = images.ControllerV11(image_service=image_service) @@ -974,11 +980,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_detail_filter_with_type(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'property-image_type': 'BASE'} image_service.index(context, filters=filters).AndReturn([]) self.mox.ReplayAll() - request = webob.Request.blank('/v1.1/images/detail?type=BASE') + request = webob.Request.blank('/v1.1/fake/images/detail?type=BASE') request.environ['nova.context'] = context controller = images.ControllerV11(image_service=image_service) controller.index(request) @@ -986,11 +992,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_detail_filter_not_supported(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {'status': 'ACTIVE'} image_service.detail(context, filters=filters).AndReturn([]) self.mox.ReplayAll() - request = webob.Request.blank('/v1.1/images/detail?status=ACTIVE&' + request = webob.Request.blank('/v1.1/fake/images/detail?status=ACTIVE&' 'UNSUPPORTEDFILTER=testname') request.environ['nova.context'] = context controller = images.ControllerV11(image_service=image_service) @@ -999,11 +1005,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_image_detail_no_filters(self): image_service = self.mox.CreateMockAnything() - context = object() + context = self._get_fake_context() filters = {} image_service.detail(context, filters=filters).AndReturn([]) self.mox.ReplayAll() - request = webob.Request.blank('/v1.1/images/detail') + request = webob.Request.blank('/v1.1/fake/images/detail') request.environ['nova.context'] = context controller = images.ControllerV11(image_service=image_service) controller.detail(request) @@ -1123,8 +1129,8 @@ class ImageXMLSerializationTest(test.TestCase): TIMESTAMP = "2010-10-11T10:30:22Z" SERVER_HREF = 'http://localhost/v1.1/servers/123' SERVER_BOOKMARK = 'http://localhost/servers/123' - IMAGE_HREF = 'http://localhost/v1.1/images/%s' - IMAGE_BOOKMARK = 'http://localhost/images/%s' + IMAGE_HREF = 'http://localhost/v1.1/fake/images/%s' + IMAGE_BOOKMARK = 'http://localhost/fake/images/%s' def test_show(self): serializer = images.ImageXMLSerializer() diff --git a/nova/tests/api/openstack/test_server_actions.py b/nova/tests/api/openstack/test_server_actions.py index 80a27e30f..90fe2f0b3 100644 --- a/nova/tests/api/openstack/test_server_actions.py +++ b/nova/tests/api/openstack/test_server_actions.py @@ -489,7 +489,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_bad_body(self): body = {} - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -498,7 +498,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_unknown_action(self): body = {'sockTheFox': {'fakekey': '1234'}} - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -509,7 +509,7 @@ class ServerActionsTestV11(test.TestCase): mock_method = MockSetAdminPassword() self.stubs.Set(nova.compute.api.API, 'set_admin_password', mock_method) body = {'changePassword': {'adminPass': '1234pass'}} - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -521,7 +521,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_change_password_xml(self): mock_method = MockSetAdminPassword() self.stubs.Set(nova.compute.api.API, 'set_admin_password', mock_method) - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = "application/xml" req.body = """<?xml version="1.0" encoding="UTF-8"?> @@ -535,7 +535,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_change_password_not_a_string(self): body = {'changePassword': {'adminPass': 1234}} - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -544,7 +544,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_change_password_bad_request(self): body = {'changePassword': {'pass': '12345'}} - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -553,7 +553,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_change_password_empty_string(self): body = {'changePassword': {'adminPass': ''}} - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -562,7 +562,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_change_password_none(self): body = {'changePassword': {'adminPass': None}} - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -571,7 +571,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_reboot_hard(self): body = dict(reboot=dict(type="HARD")) - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -580,7 +580,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_reboot_soft(self): body = dict(reboot=dict(type="SOFT")) - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -589,7 +589,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_reboot_incorrect_type(self): body = dict(reboot=dict(type="NOT_A_TYPE")) - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -598,7 +598,7 @@ class ServerActionsTestV11(test.TestCase): def test_server_reboot_missing_type(self): body = dict(reboot=dict()) - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -612,7 +612,7 @@ class ServerActionsTestV11(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -633,7 +633,7 @@ class ServerActionsTestV11(test.TestCase): self.stubs.Set(nova.db, 'instance_get_by_uuid', return_server_with_uuid_and_power_state(state)) - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -651,7 +651,7 @@ class ServerActionsTestV11(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -667,7 +667,7 @@ class ServerActionsTestV11(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -682,7 +682,7 @@ class ServerActionsTestV11(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -701,7 +701,7 @@ class ServerActionsTestV11(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -720,7 +720,7 @@ class ServerActionsTestV11(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.content_type = 'application/json' req.body = json.dumps(body) @@ -730,7 +730,7 @@ class ServerActionsTestV11(test.TestCase): def test_resize_server(self): - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.content_type = 'application/json' req.method = 'POST' body_dict = dict(resize=dict(flavorRef="http://localhost/3")) @@ -748,7 +748,7 @@ class ServerActionsTestV11(test.TestCase): self.assertEqual(self.resize_called, True) def test_resize_server_no_flavor(self): - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.content_type = 'application/json' req.method = 'POST' body_dict = dict(resize=dict()) @@ -758,7 +758,7 @@ class ServerActionsTestV11(test.TestCase): self.assertEqual(res.status_int, 400) def test_resize_server_no_flavor_ref(self): - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.content_type = 'application/json' req.method = 'POST' body_dict = dict(resize=dict(flavorRef=None)) @@ -768,7 +768,7 @@ class ServerActionsTestV11(test.TestCase): self.assertEqual(res.status_int, 400) def test_confirm_resize_server(self): - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.content_type = 'application/json' req.method = 'POST' body_dict = dict(confirmResize=None) @@ -786,7 +786,7 @@ class ServerActionsTestV11(test.TestCase): self.assertEqual(self.confirm_resize_called, True) def test_revert_resize_server(self): - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.content_type = 'application/json' req.method = 'POST' body_dict = dict(revertResize=None) @@ -809,7 +809,7 @@ class ServerActionsTestV11(test.TestCase): 'name': 'Snapshot 1', }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -828,7 +828,7 @@ class ServerActionsTestV11(test.TestCase): 'name': 'Snapshot 1', }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -842,7 +842,7 @@ class ServerActionsTestV11(test.TestCase): 'metadata': {'key': 'asdf'}, }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -860,7 +860,7 @@ class ServerActionsTestV11(test.TestCase): } for num in range(FLAGS.quota_metadata_items + 1): body['createImage']['metadata']['foo%i' % num] = "bar" - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -871,7 +871,7 @@ class ServerActionsTestV11(test.TestCase): body = { 'createImage': {}, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -885,7 +885,7 @@ class ServerActionsTestV11(test.TestCase): 'metadata': 'henry', }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -904,7 +904,7 @@ class ServerActionsTestV11(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers/1/action') + req = webob.Request.blank('/v1.1/fake/servers/1/action') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" diff --git a/nova/tests/api/openstack/test_server_metadata.py b/nova/tests/api/openstack/test_server_metadata.py index 8512bd518..296bbd9dc 100644 --- a/nova/tests/api/openstack/test_server_metadata.py +++ b/nova/tests/api/openstack/test_server_metadata.py @@ -83,7 +83,7 @@ class ServerMetaDataTest(test.TestCase): def test_index(self): self.stubs.Set(nova.db.api, 'instance_metadata_get', return_server_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') res = req.get_response(fakes.wsgi_app()) self.assertEqual(200, res.status_int) res_dict = json.loads(res.body) @@ -100,7 +100,7 @@ class ServerMetaDataTest(test.TestCase): def test_index_xml(self): self.stubs.Set(nova.db.api, 'instance_metadata_get', return_server_metadata) - request = webob.Request.blank("/v1.1/servers/1/metadata") + request = webob.Request.blank("/v1.1/fake/servers/1/metadata") request.accept = "application/xml" response = request.get_response(fakes.wsgi_app()) self.assertEqual(200, response.status_int) @@ -120,14 +120,14 @@ class ServerMetaDataTest(test.TestCase): def test_index_nonexistant_server(self): self.stubs.Set(nova.db.api, 'instance_get', return_server_nonexistant) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') res = req.get_response(fakes.wsgi_app()) self.assertEqual(404, res.status_int) def test_index_no_data(self): self.stubs.Set(nova.db.api, 'instance_metadata_get', return_empty_server_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') res = req.get_response(fakes.wsgi_app()) self.assertEqual(200, res.status_int) res_dict = json.loads(res.body) @@ -137,7 +137,7 @@ class ServerMetaDataTest(test.TestCase): def test_show(self): self.stubs.Set(nova.db.api, 'instance_metadata_get', return_server_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/key2') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key2') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(200, res.status_int) @@ -147,7 +147,7 @@ class ServerMetaDataTest(test.TestCase): def test_show_xml(self): self.stubs.Set(nova.db.api, 'instance_metadata_get', return_server_metadata) - request = webob.Request.blank("/v1.1/servers/1/metadata/key2") + request = webob.Request.blank("/v1.1/fake/servers/1/metadata/key2") request.accept = "application/xml" response = request.get_response(fakes.wsgi_app()) self.assertEqual(200, response.status_int) @@ -164,14 +164,14 @@ class ServerMetaDataTest(test.TestCase): def test_show_nonexistant_server(self): self.stubs.Set(nova.db.api, 'instance_get', return_server_nonexistant) - req = webob.Request.blank('/v1.1/servers/1/metadata/key2') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key2') res = req.get_response(fakes.wsgi_app()) self.assertEqual(404, res.status_int) def test_show_meta_not_found(self): self.stubs.Set(nova.db.api, 'instance_metadata_get', return_empty_server_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/key6') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key6') res = req.get_response(fakes.wsgi_app()) self.assertEqual(404, res.status_int) @@ -180,7 +180,7 @@ class ServerMetaDataTest(test.TestCase): return_server_metadata) self.stubs.Set(nova.db.api, 'instance_metadata_delete', delete_server_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/key2') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key2') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) self.assertEqual(204, res.status_int) @@ -188,7 +188,7 @@ class ServerMetaDataTest(test.TestCase): def test_delete_nonexistant_server(self): self.stubs.Set(nova.db.api, 'instance_get', return_server_nonexistant) - req = webob.Request.blank('/v1.1/servers/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key1') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) self.assertEqual(404, res.status_int) @@ -196,7 +196,7 @@ class ServerMetaDataTest(test.TestCase): def test_delete_meta_not_found(self): self.stubs.Set(nova.db.api, 'instance_metadata_get', return_empty_server_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/key6') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key6') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) self.assertEqual(404, res.status_int) @@ -206,7 +206,7 @@ class ServerMetaDataTest(test.TestCase): return_server_metadata) self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') req.method = 'POST' req.content_type = "application/json" input = {"metadata": {"key9": "value9"}} @@ -227,7 +227,7 @@ class ServerMetaDataTest(test.TestCase): return_server_metadata) self.stubs.Set(nova.db.api, "instance_metadata_update", return_create_instance_metadata) - req = webob.Request.blank("/v1.1/servers/1/metadata") + req = webob.Request.blank("/v1.1/fake/servers/1/metadata") req.method = "POST" req.content_type = "application/xml" req.accept = "application/xml" @@ -258,7 +258,7 @@ class ServerMetaDataTest(test.TestCase): def test_create_empty_body(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') req.method = 'POST' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) @@ -266,7 +266,7 @@ class ServerMetaDataTest(test.TestCase): def test_create_nonexistant_server(self): self.stubs.Set(nova.db.api, 'instance_get', return_server_nonexistant) - req = webob.Request.blank('/v1.1/servers/100/metadata') + req = webob.Request.blank('/v1.1/fake/servers/100/metadata') req.method = 'POST' req.body = '{"metadata": {"key1": "value1"}}' req.headers["content-type"] = "application/json" @@ -276,7 +276,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_all(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') req.method = 'PUT' req.content_type = "application/json" expected = { @@ -294,7 +294,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_all_empty_container(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') req.method = 'PUT' req.content_type = "application/json" expected = {'metadata': {}} @@ -307,7 +307,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_all_malformed_container(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') req.method = 'PUT' req.content_type = "application/json" expected = {'meta': {}} @@ -318,7 +318,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_all_malformed_data(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') req.method = 'PUT' req.content_type = "application/json" expected = {'metadata': ['asdf']} @@ -328,7 +328,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_all_nonexistant_server(self): self.stubs.Set(nova.db.api, 'instance_get', return_server_nonexistant) - req = webob.Request.blank('/v1.1/servers/100/metadata') + req = webob.Request.blank('/v1.1/fake/servers/100/metadata') req.method = 'PUT' req.content_type = "application/json" req.body = json.dumps({'metadata': {'key10': 'value10'}}) @@ -338,7 +338,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_item(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key1') req.method = 'PUT' req.body = '{"meta": {"key1": "value1"}}' req.headers["content-type"] = "application/json" @@ -352,7 +352,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_item_xml(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/key9') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key9') req.method = 'PUT' req.accept = "application/json" req.content_type = "application/xml" @@ -369,7 +369,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_item_nonexistant_server(self): self.stubs.Set(nova.db.api, 'instance_get', return_server_nonexistant) - req = webob.Request.blank('/v1.1/servers/asdf/metadata/key1') + req = webob.Request.blank('/v1.1/fake/servers/asdf/metadata/key1') req.method = 'PUT' req.body = '{"meta":{"key1": "value1"}}' req.headers["content-type"] = "application/json" @@ -379,7 +379,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_item_empty_body(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key1') req.method = 'PUT' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) @@ -388,7 +388,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_item_too_many_keys(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key1') req.method = 'PUT' req.body = '{"meta": {"key1": "value1", "key2": "value2"}}' req.headers["content-type"] = "application/json" @@ -398,7 +398,7 @@ class ServerMetaDataTest(test.TestCase): def test_update_item_body_uri_mismatch(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata) - req = webob.Request.blank('/v1.1/servers/1/metadata/bad') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/bad') req.method = 'PUT' req.body = '{"meta": {"key1": "value1"}}' req.headers["content-type"] = "application/json" @@ -412,7 +412,7 @@ class ServerMetaDataTest(test.TestCase): for num in range(FLAGS.quota_metadata_items + 1): data['metadata']['key%i' % num] = "blah" json_string = str(data).replace("\'", "\"") - req = webob.Request.blank('/v1.1/servers/1/metadata') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata') req.method = 'POST' req.body = json_string req.headers["content-type"] = "application/json" @@ -422,7 +422,7 @@ class ServerMetaDataTest(test.TestCase): def test_too_many_metadata_items_on_update_item(self): self.stubs.Set(nova.db.api, 'instance_metadata_update', return_create_instance_metadata_max) - req = webob.Request.blank('/v1.1/servers/1/metadata/key1') + req = webob.Request.blank('/v1.1/fake/servers/1/metadata/key1') req.method = 'PUT' req.body = '{"meta": {"a new key": "a new value"}}' req.headers["content-type"] = "application/json" diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 7fc1122d0..dd4b63a2c 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -19,6 +19,7 @@ import base64 import datetime import json import unittest +from lxml import etree from xml.dom import minidom import webob @@ -32,6 +33,7 @@ import nova.api.openstack from nova.api.openstack import create_instance_helper from nova.api.openstack import servers from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil import nova.compute.api from nova.compute import instance_types from nova.compute import power_state @@ -46,6 +48,8 @@ from nova.tests.api.openstack import fakes FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' +NS = "{http://docs.openstack.org/compute/api/v1.1}" +ATOMNS = "{http://www.w3.org/2005/Atom}" def fake_gen_uuid(): @@ -145,7 +149,8 @@ def instance_addresses(context, instance_id): def stub_instance(id, user_id='fake', project_id='fake', private_address=None, public_addresses=None, host=None, power_state=0, reservation_id="", uuid=FAKE_UUID, image_ref="10", - flavor_id="1", interfaces=None, name=None): + flavor_id="1", interfaces=None, name=None, + access_ipv4=None, access_ipv6=None): metadata = [] metadata.append(InstanceMetadata(key='seq', value=id)) @@ -197,6 +202,8 @@ def stub_instance(id, user_id='fake', project_id='fake', private_address=None, "display_description": "", "locked": False, "metadata": metadata, + "access_ip_v4": access_ipv4, + "access_ip_v6": access_ipv6, "uuid": uuid, "virtual_interfaces": interfaces} @@ -297,10 +304,10 @@ class ServersTest(test.TestCase): self.assertEqual(res_dict['server']['name'], 'server1') def test_get_server_by_id_v1_1(self): - image_bookmark = "http://localhost/images/10" - flavor_ref = "http://localhost/v1.1/flavors/1" + image_bookmark = "http://localhost/fake/images/10" + flavor_ref = "http://localhost/v1.1/fake/flavors/1" flavor_id = "1" - flavor_bookmark = "http://localhost/flavors/1" + flavor_bookmark = "http://localhost/fake/flavors/1" public_ip = '192.168.0.3' private_ip = '172.19.0.1' @@ -322,7 +329,7 @@ class ServersTest(test.TestCase): interfaces=interfaces) self.stubs.Set(nova.db.api, 'instance_get', new_return_server) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) expected_server = { @@ -334,6 +341,8 @@ class ServersTest(test.TestCase): "progress": 0, "name": "server1", "status": "BUILD", + "accessIPv4": "", + "accessIPv6": "", "hostId": '', "image": { "id": "10", @@ -374,11 +383,11 @@ class ServersTest(test.TestCase): { "rel": "self", #FIXME(wwolf) Do we want the links to be id or uuid? - "href": "http://localhost/v1.1/servers/1", + "href": "http://localhost/v1.1/fake/servers/1", }, { "rel": "bookmark", - "href": "http://localhost/servers/1", + "href": "http://localhost/fake/servers/1", }, ], } @@ -387,12 +396,12 @@ class ServersTest(test.TestCase): self.assertDictMatch(res_dict, expected_server) def test_get_server_by_id_v1_1_xml(self): - image_bookmark = "http://localhost/images/10" - flavor_ref = "http://localhost/v1.1/flavors/1" + image_bookmark = "http://localhost/fake/images/10" + flavor_ref = "http://localhost/v1.1/fake/flavors/1" flavor_id = "1" - flavor_bookmark = "http://localhost/flavors/1" - server_href = "http://localhost/v1.1/servers/1" - server_bookmark = "http://localhost/servers/1" + flavor_bookmark = "http://localhost/fake/flavors/1" + server_href = "http://localhost/v1.1/fake/servers/1" + server_bookmark = "http://localhost/fake/servers/1" public_ip = '192.168.0.3' private_ip = '172.19.0.1' @@ -414,7 +423,7 @@ class ServersTest(test.TestCase): interfaces=interfaces) self.stubs.Set(nova.db.api, 'instance_get', new_return_server) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') req.headers['Accept'] = 'application/xml' res = req.get_response(fakes.wsgi_app()) actual = minidom.parseString(res.body.replace(' ', '')) @@ -431,6 +440,8 @@ class ServersTest(test.TestCase): created="%(expected_created)s" hostId="" status="BUILD" + accessIPv4="" + accessIPv6="" progress="0"> <atom:link href="%(server_href)s" rel="self"/> <atom:link href="%(server_bookmark)s" rel="bookmark"/> @@ -459,10 +470,10 @@ class ServersTest(test.TestCase): self.assertEqual(expected.toxml(), actual.toxml()) def test_get_server_with_active_status_by_id_v1_1(self): - image_bookmark = "http://localhost/images/10" - flavor_ref = "http://localhost/v1.1/flavors/1" + image_bookmark = "http://localhost/fake/images/10" + flavor_ref = "http://localhost/v1.1/fake/flavors/1" flavor_id = "1" - flavor_bookmark = "http://localhost/flavors/1" + flavor_bookmark = "http://localhost/fake/flavors/1" private_ip = "192.168.0.3" public_ip = "1.2.3.4" @@ -484,7 +495,7 @@ class ServersTest(test.TestCase): interfaces=interfaces, power_state=1) self.stubs.Set(nova.db.api, 'instance_get', new_return_server) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) expected_server = { @@ -496,6 +507,8 @@ class ServersTest(test.TestCase): "progress": 100, "name": "server1", "status": "ACTIVE", + "accessIPv4": "", + "accessIPv6": "", "hostId": '', "image": { "id": "10", @@ -535,11 +548,11 @@ class ServersTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/servers/1", + "href": "http://localhost/v1.1/fake/servers/1", }, { "rel": "bookmark", - "href": "http://localhost/servers/1", + "href": "http://localhost/fake/servers/1", }, ], } @@ -549,10 +562,10 @@ class ServersTest(test.TestCase): def test_get_server_with_id_image_ref_by_id_v1_1(self): image_ref = "10" - image_bookmark = "http://localhost/images/10" - flavor_ref = "http://localhost/v1.1/flavors/1" + image_bookmark = "http://localhost/fake/images/10" + flavor_ref = "http://localhost/v1.1/fake/flavors/1" flavor_id = "1" - flavor_bookmark = "http://localhost/flavors/1" + flavor_bookmark = "http://localhost/fake/flavors/1" private_ip = "192.168.0.3" public_ip = "1.2.3.4" @@ -575,7 +588,7 @@ class ServersTest(test.TestCase): flavor_id=flavor_id) self.stubs.Set(nova.db.api, 'instance_get', new_return_server) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) expected_server = { @@ -587,6 +600,8 @@ class ServersTest(test.TestCase): "progress": 100, "name": "server1", "status": "ACTIVE", + "accessIPv4": "", + "accessIPv6": "", "hostId": '', "image": { "id": "10", @@ -626,11 +641,11 @@ class ServersTest(test.TestCase): "links": [ { "rel": "self", - "href": "http://localhost/v1.1/servers/1", + "href": "http://localhost/v1.1/fake/servers/1", }, { "rel": "bookmark", - "href": "http://localhost/servers/1", + "href": "http://localhost/fake/servers/1", }, ], } @@ -775,7 +790,7 @@ class ServersTest(test.TestCase): interfaces=interfaces) self.stubs.Set(nova.db.api, 'instance_get', new_return_server) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -819,7 +834,7 @@ class ServersTest(test.TestCase): interfaces=interfaces) self.stubs.Set(nova.db.api, 'instance_get', new_return_server) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -869,7 +884,7 @@ class ServersTest(test.TestCase): 'virtual_interface_get_by_instance', _return_vifs) - req = webob.Request.blank('/v1.1/servers/1/ips') + req = webob.Request.blank('/v1.1/fake/servers/1/ips') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -919,7 +934,7 @@ class ServersTest(test.TestCase): 'virtual_interface_get_by_instance', _return_vifs) - req = webob.Request.blank('/v1.1/servers/1/ips/network_2') + req = webob.Request.blank('/v1.1/fake/servers/1/ips/network_2') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) res_dict = json.loads(res.body) @@ -939,7 +954,7 @@ class ServersTest(test.TestCase): 'virtual_interface_get_by_instance', _return_vifs) - req = webob.Request.blank('/v1.1/servers/1/ips/network_0') + req = webob.Request.blank('/v1.1/fake/servers/1/ips/network_0') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 404) @@ -949,7 +964,7 @@ class ServersTest(test.TestCase): 'virtual_interface_get_by_instance', _return_vifs) - req = webob.Request.blank('/v1.1/servers/600/ips') + req = webob.Request.blank('/v1.1/fake/servers/600/ips') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 404) @@ -1018,7 +1033,7 @@ class ServersTest(test.TestCase): i += 1 def test_get_server_list_v1_1(self): - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/fake/servers') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -1031,11 +1046,11 @@ class ServersTest(test.TestCase): expected_links = [ { "rel": "self", - "href": "http://localhost/v1.1/servers/%s" % s['id'], + "href": "http://localhost/v1.1/fake/servers/%s" % s['id'], }, { "rel": "bookmark", - "href": "http://localhost/servers/%s" % s['id'], + "href": "http://localhost/fake/servers/%s" % s['id'], }, ] @@ -1082,19 +1097,19 @@ class ServersTest(test.TestCase): self.assertTrue(res.body.find('offset param') > -1) def test_get_servers_with_marker(self): - req = webob.Request.blank('/v1.1/servers?marker=2') + req = webob.Request.blank('/v1.1/fake/servers?marker=2') res = req.get_response(fakes.wsgi_app()) servers = json.loads(res.body)['servers'] self.assertEqual([s['name'] for s in servers], ["server3", "server4"]) def test_get_servers_with_limit_and_marker(self): - req = webob.Request.blank('/v1.1/servers?limit=2&marker=1') + req = webob.Request.blank('/v1.1/fake/servers?limit=2&marker=1') res = req.get_response(fakes.wsgi_app()) servers = json.loads(res.body)['servers'] self.assertEqual([s['name'] for s in servers], ['server2', 'server3']) def test_get_servers_with_bad_marker(self): - req = webob.Request.blank('/v1.1/servers?limit=2&marker=asdf') + req = webob.Request.blank('/v1.1/fake/servers?limit=2&marker=asdf') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) self.assertTrue(res.body.find('marker param') > -1) @@ -1120,7 +1135,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - req = webob.Request.blank('/v1.1/servers?unknownoption=whee') + req = webob.Request.blank('/v1.1/fake/servers?unknownoption=whee') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) servers = json.loads(res.body)['servers'] @@ -1137,7 +1152,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) self.flags(allow_admin_api=False) - req = webob.Request.blank('/v1.1/servers?image=12345') + req = webob.Request.blank('/v1.1/fake/servers?image=12345') res = req.get_response(fakes.wsgi_app()) # The following assert will fail if either of the asserts in # fake_get_all() fail @@ -1157,7 +1172,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) self.flags(allow_admin_api=False) - req = webob.Request.blank('/v1.1/servers?flavor=12345') + req = webob.Request.blank('/v1.1/fake/servers?flavor=12345') res = req.get_response(fakes.wsgi_app()) # The following assert will fail if either of the asserts in # fake_get_all() fail @@ -1177,7 +1192,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) self.flags(allow_admin_api=False) - req = webob.Request.blank('/v1.1/servers?status=active') + req = webob.Request.blank('/v1.1/fake/servers?status=active') res = req.get_response(fakes.wsgi_app()) # The following assert will fail if either of the asserts in # fake_get_all() fail @@ -1191,7 +1206,7 @@ class ServersTest(test.TestCase): self.flags(allow_admin_api=False) - req = webob.Request.blank('/v1.1/servers?status=running') + req = webob.Request.blank('/v1.1/fake/servers?status=running') res = req.get_response(fakes.wsgi_app()) # The following assert will fail if either of the asserts in # fake_get_all() fail @@ -1208,7 +1223,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) self.flags(allow_admin_api=False) - req = webob.Request.blank('/v1.1/servers?name=whee.*') + req = webob.Request.blank('/v1.1/fake/servers?name=whee.*') res = req.get_response(fakes.wsgi_app()) # The following assert will fail if either of the asserts in # fake_get_all() fail @@ -1239,7 +1254,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = webob.Request.blank('/v1.1/servers?%s' % query_str) + req = webob.Request.blank('/v1.1/fake/servers?%s' % query_str) # Request admin context context = nova.context.RequestContext('testuser', 'testproject', is_admin=True) @@ -1273,7 +1288,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = webob.Request.blank('/v1.1/servers?%s' % query_str) + req = webob.Request.blank('/v1.1/fake/servers?%s' % query_str) # Request admin context context = nova.context.RequestContext('testuser', 'testproject', is_admin=False) @@ -1306,7 +1321,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = webob.Request.blank('/v1.1/servers?%s' % query_str) + req = webob.Request.blank('/v1.1/fake/servers?%s' % query_str) # Request admin context context = nova.context.RequestContext('testuser', 'testproject', is_admin=True) @@ -1332,7 +1347,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - req = webob.Request.blank('/v1.1/servers?ip=10\..*') + req = webob.Request.blank('/v1.1/fake/servers?ip=10\..*') # Request admin context context = nova.context.RequestContext('testuser', 'testproject', is_admin=True) @@ -1358,7 +1373,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - req = webob.Request.blank('/v1.1/servers?ip6=ffff.*') + req = webob.Request.blank('/v1.1/fake/servers?ip6=ffff.*') # Request admin context context = nova.context.RequestContext('testuser', 'testproject', is_admin=True) @@ -1379,6 +1394,8 @@ class ServersTest(test.TestCase): 'display_name': 'server_test', 'uuid': FAKE_UUID, 'instance_type': dict(inst_type), + 'access_ip_v4': '1.2.3.4', + 'access_ip_v6': 'fead::1234', 'image_ref': image_ref, "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), @@ -1556,18 +1573,105 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 422) + def test_create_instance_whitespace_name(self): + self._setup_for_create_instance() + + body = { + 'server': { + 'name': ' ', + 'imageId': 3, + 'flavorId': 1, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + }, + } + + req = webob.Request.blank('/v1.0/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) + + def test_create_instance_with_access_ip_v1_1(self): + self._setup_for_create_instance() + + # proper local hrefs must start with 'http://localhost/v1.1/' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/123/flavors/3' + access_ipv4 = '1.2.3.4' + access_ipv6 = 'fead::1234' + expected_flavor = { + "id": "3", + "links": [ + { + "rel": "bookmark", + "href": 'http://localhost/123/flavors/3', + }, + ], + } + expected_image = { + "id": "2", + "links": [ + { + "rel": "bookmark", + "href": 'http://localhost/123/images/2', + }, + ], + } + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'accessIPv4': access_ipv4, + 'accessIPv6': access_ipv6, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "MQ==", + }, + ], + }, + } + + req = webob.Request.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + + self.assertEqual(res.status_int, 202) + server = json.loads(res.body)['server'] + self.assertEqual(16, len(server['adminPass'])) + self.assertEqual(1, server['id']) + self.assertEqual(0, server['progress']) + self.assertEqual('server_test', server['name']) + self.assertEqual(expected_flavor, server['flavor']) + self.assertEqual(expected_image, server['image']) + self.assertEqual(access_ipv4, server['accessIPv4']) + self.assertEqual(access_ipv6, server['accessIPv6']) + def test_create_instance_v1_1(self): self._setup_for_create_instance() # proper local hrefs must start with 'http://localhost/v1.1/' image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/flavors/3' + flavor_ref = 'http://localhost/123/flavors/3' expected_flavor = { "id": "3", "links": [ { "rel": "bookmark", - "href": 'http://localhost/flavors/3', + "href": 'http://localhost/fake/flavors/3', }, ], } @@ -1576,7 +1680,7 @@ class ServersTest(test.TestCase): "links": [ { "rel": "bookmark", - "href": 'http://localhost/images/2', + "href": 'http://localhost/fake/images/2', }, ], } @@ -1598,7 +1702,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/fake/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1613,6 +1717,8 @@ class ServersTest(test.TestCase): self.assertEqual('server_test', server['name']) self.assertEqual(expected_flavor, server['flavor']) self.assertEqual(expected_image, server['image']) + self.assertEqual('1.2.3.4', server['accessIPv4']) + self.assertEqual('fead::1234', server['accessIPv6']) def test_create_instance_v1_1_invalid_flavor_href(self): self._setup_for_create_instance() @@ -1623,7 +1729,7 @@ class ServersTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/fake/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1633,13 +1739,13 @@ class ServersTest(test.TestCase): def test_create_instance_v1_1_invalid_flavor_id_int(self): self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' + image_href = 'http://localhost/v1.1/123/images/2' flavor_ref = -1 body = dict(server=dict( name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1655,7 +1761,7 @@ class ServersTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/fake/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1671,7 +1777,7 @@ class ServersTest(test.TestCase): name='server_test', imageRef=image_href, flavorRef=flavor_ref, metadata={'hello': 'world', 'open': 'stack'}, personality={})) - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/fake/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1688,7 +1794,7 @@ class ServersTest(test.TestCase): "links": [ { "rel": "bookmark", - "href": 'http://localhost/flavors/3', + "href": 'http://localhost/fake/flavors/3', }, ], } @@ -1697,7 +1803,7 @@ class ServersTest(test.TestCase): "links": [ { "rel": "bookmark", - "href": 'http://localhost/images/2', + "href": 'http://localhost/fake/images/2', }, ], } @@ -1709,7 +1815,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/fake/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1756,7 +1862,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/fake/servers') req.method = 'POST' req.body = json.dumps(body) req.headers['content-type'] = "application/json" @@ -1777,7 +1883,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/fake/servers') req.method = 'POST' req.body = json.dumps(body) req.headers['content-type'] = "application/json" @@ -1874,15 +1980,37 @@ class ServersTest(test.TestCase): self.assertEqual(mock_method.password, 'bacon') def test_update_server_no_body_v1_1(self): - req = webob.Request.blank('/v1.0/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') req.method = 'PUT' res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) + def test_update_server_all_attributes_v1_1(self): + self.stubs.Set(nova.db.api, 'instance_get', + return_server_with_attributes(name='server_test', + access_ipv4='0.0.0.0', + access_ipv6='beef::0123')) + req = webob.Request.blank('/v1.1/123/servers/1') + req.method = 'PUT' + req.content_type = 'application/json' + body = {'server': { + 'name': 'server_test', + 'accessIPv4': '0.0.0.0', + 'accessIPv6': 'beef::0123', + }} + req.body = json.dumps(body) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + self.assertEqual(res_dict['server']['id'], 1) + self.assertEqual(res_dict['server']['name'], 'server_test') + self.assertEqual(res_dict['server']['accessIPv4'], '0.0.0.0') + self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') + def test_update_server_name_v1_1(self): self.stubs.Set(nova.db.api, 'instance_get', return_server_with_attributes(name='server_test')) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') req.method = 'PUT' req.content_type = 'application/json' req.body = json.dumps({'server': {'name': 'server_test'}}) @@ -1892,6 +2020,32 @@ class ServersTest(test.TestCase): self.assertEqual(res_dict['server']['id'], 1) self.assertEqual(res_dict['server']['name'], 'server_test') + def test_update_server_access_ipv4_v1_1(self): + self.stubs.Set(nova.db.api, 'instance_get', + return_server_with_attributes(access_ipv4='0.0.0.0')) + req = webob.Request.blank('/v1.1/123/servers/1') + req.method = 'PUT' + req.content_type = 'application/json' + req.body = json.dumps({'server': {'accessIPv4': '0.0.0.0'}}) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + self.assertEqual(res_dict['server']['id'], 1) + self.assertEqual(res_dict['server']['accessIPv4'], '0.0.0.0') + + def test_update_server_access_ipv6_v1_1(self): + self.stubs.Set(nova.db.api, 'instance_get', + return_server_with_attributes(access_ipv6='beef::0123')) + req = webob.Request.blank('/v1.1/123/servers/1') + req.method = 'PUT' + req.content_type = 'application/json' + req.body = json.dumps({'server': {'accessIPv6': 'beef::0123'}}) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + self.assertEqual(res_dict['server']['id'], 1) + self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') + def test_update_server_adminPass_ignored_v1_1(self): inst_dict = dict(name='server_test', adminPass='bacon') self.body = json.dumps(dict(server=inst_dict)) @@ -1905,7 +2059,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_get', return_server_with_attributes(name='server_test')) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') req.method = 'PUT' req.content_type = "application/json" req.body = self.body @@ -1938,7 +2092,7 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 501) def test_server_backup_schedule_deprecated_v1_1(self): - req = webob.Request.blank('/v1.1/servers/1/backup_schedule') + req = webob.Request.blank('/v1.1/fake/servers/1/backup_schedule') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 404) @@ -1978,7 +2132,7 @@ class ServersTest(test.TestCase): "links": [ { "rel": "bookmark", - "href": 'http://localhost/flavors/1', + "href": 'http://localhost/fake/flavors/1', }, ], } @@ -1987,11 +2141,11 @@ class ServersTest(test.TestCase): "links": [ { "rel": "bookmark", - "href": 'http://localhost/images/10', + "href": 'http://localhost/fake/images/10', }, ], } - req = webob.Request.blank('/v1.1/servers/detail') + req = webob.Request.blank('/v1.1/fake/servers/detail') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -2150,7 +2304,7 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 422) def test_delete_server_instance_v1_1(self): - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') req.method = 'DELETE' self.server_delete_called = False @@ -2491,6 +2645,62 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): } self.assertEquals(request['body'], expected) + def test_access_ipv4(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="2" + accessIPv4="1.2.3.4"/>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "accessIPv4": "1.2.3.4", + }, + } + self.assertEquals(request['body'], expected) + + def test_access_ipv6(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="2" + accessIPv6="fead::1234"/>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "accessIPv6": "fead::1234", + }, + } + self.assertEquals(request['body'], expected) + + def test_access_ip(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="2" + accessIPv4="1.2.3.4" + accessIPv6="fead::1234"/>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + }, + } + self.assertEquals(request['body'], expected) + def test_admin_pass(self): serial_request = """ <server xmlns="http://docs.openstack.org/compute/api/v1.1" @@ -3199,24 +3409,28 @@ class ServersViewBuilderV11Test(test.TestCase): "display_description": "", "locked": False, "metadata": [], + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", #"address": , #"floating_ips": [{"address":ip} for ip in public_addresses]} "uuid": "deadbeef-feed-edee-beef-d0ea7beefedd"} return instance - def _get_view_builder(self): + def _get_view_builder(self, project_id=""): base_url = "http://localhost/v1.1" views = nova.api.openstack.views address_builder = views.addresses.ViewBuilderV11() - flavor_builder = views.flavors.ViewBuilderV11(base_url) - image_builder = views.images.ViewBuilderV11(base_url) + flavor_builder = views.flavors.ViewBuilderV11(base_url, project_id) + image_builder = views.images.ViewBuilderV11(base_url, project_id) view_builder = nova.api.openstack.views.servers.ViewBuilderV11( address_builder, flavor_builder, image_builder, - base_url) + base_url, + project_id, + ) return view_builder def test_build_server(self): @@ -3241,6 +3455,29 @@ class ServersViewBuilderV11Test(test.TestCase): output = self.view_builder.build(self.instance, False) self.assertDictMatch(output, expected_server) + def test_build_server_with_project_id(self): + expected_server = { + "server": { + "id": 1, + "uuid": self.instance['uuid'], + "name": "test_server", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/servers/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/servers/1", + }, + ], + } + } + + view_builder = self._get_view_builder(project_id='fake') + output = view_builder.build(self.instance, False) + self.assertDictMatch(output, expected_server) + def test_build_server_detail(self): image_bookmark = "http://localhost/images/5" flavor_bookmark = "http://localhost/flavors/1" @@ -3253,6 +3490,8 @@ class ServersViewBuilderV11Test(test.TestCase): "progress": 0, "name": "test_server", "status": "BUILD", + "accessIPv4": "", + "accessIPv6": "", "hostId": '', "image": { "id": "5", @@ -3304,6 +3543,8 @@ class ServersViewBuilderV11Test(test.TestCase): "progress": 100, "name": "test_server", "status": "ACTIVE", + "accessIPv4": "", + "accessIPv6": "", "hostId": '', "image": { "id": "5", @@ -3341,6 +3582,114 @@ class ServersViewBuilderV11Test(test.TestCase): output = self.view_builder.build(self.instance, True) self.assertDictMatch(output, expected_server) + def test_build_server_detail_with_accessipv4(self): + + self.instance['access_ip_v4'] = '1.2.3.4' + + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + expected_server = { + "server": { + "id": 1, + "uuid": self.instance['uuid'], + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": '', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": {}, + "metadata": {}, + "accessIPv4": "1.2.3.4", + "accessIPv6": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/servers/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/servers/1", + }, + ], + } + } + + output = self.view_builder.build(self.instance, True) + self.assertDictMatch(output, expected_server) + + def test_build_server_detail_with_accessipv6(self): + + self.instance['access_ip_v6'] = 'fead::1234' + + image_bookmark = "http://localhost/images/5" + flavor_bookmark = "http://localhost/flavors/1" + expected_server = { + "server": { + "id": 1, + "uuid": self.instance['uuid'], + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": '', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": {}, + "metadata": {}, + "accessIPv4": "", + "accessIPv6": "fead::1234", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/servers/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/servers/1", + }, + ], + } + } + + output = self.view_builder.build(self.instance, True) + self.assertDictMatch(output, expected_server) + def test_build_server_detail_with_metadata(self): metadata = [] @@ -3359,6 +3708,8 @@ class ServersViewBuilderV11Test(test.TestCase): "progress": 0, "name": "test_server", "status": "BUILD", + "accessIPv4": "", + "accessIPv6": "", "hostId": '', "image": { "id": "5", @@ -3425,6 +3776,8 @@ class ServerXMLSerializationTest(test.TestCase): "name": "test_server", "status": "BUILD", "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", "image": { "id": "5", "links": [ @@ -3483,7 +3836,9 @@ class ServerXMLSerializationTest(test.TestCase): } output = serializer.serialize(fixture, 'show') - actual = minidom.parseString(output.replace(" ", "")) + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'server') expected_server_href = self.SERVER_HREF expected_server_bookmark = self.SERVER_BOOKMARK @@ -3491,47 +3846,57 @@ class ServerXMLSerializationTest(test.TestCase): expected_flavor_bookmark = self.FLAVOR_BOOKMARK expected_now = self.TIMESTAMP expected_uuid = FAKE_UUID - expected = minidom.parseString(""" - <server id="1" - uuid="%(expected_uuid)s" - xmlns="http://docs.openstack.org/compute/api/v1.1" - xmlns:atom="http://www.w3.org/2005/Atom" - name="test_server" - updated="%(expected_now)s" - created="%(expected_now)s" - hostId="e4d909c290d0fb1ca068ffaddf22cbd0" - status="BUILD" - progress="0"> - <atom:link href="%(expected_server_href)s" rel="self"/> - <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> - <image id="5"> - <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> - </image> - <flavor id="1"> - <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> - </flavor> - <metadata> - <meta key="Open"> - Stack - </meta> - <meta key="Number"> - 1 - </meta> - </metadata> - <addresses> - <network id="network_one"> - <ip version="4" addr="67.23.10.138"/> - <ip version="6" addr="::babe:67.23.10.138"/> - </network> - <network id="network_two"> - <ip version="4" addr="67.23.10.139"/> - <ip version="6" addr="::babe:67.23.10.139"/> - </network> - </addresses> - </server> - """.replace(" ", "") % (locals())) - - self.assertEqual(expected.toxml(), actual.toxml()) + server_dict = fixture['server'] + + for key in ['name', 'id', 'uuid', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6']: + self.assertEqual(root.get(key), str(server_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 2) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + image_root = root.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = root.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = root.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) def test_create(self): serializer = servers.ServerXMLSerializer() @@ -3545,6 +3910,8 @@ class ServerXMLSerializationTest(test.TestCase): "progress": 0, "name": "test_server", "status": "BUILD", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", "adminPass": "test_password", "image": { @@ -3605,7 +3972,9 @@ class ServerXMLSerializationTest(test.TestCase): } output = serializer.serialize(fixture, 'create') - actual = minidom.parseString(output.replace(" ", "")) + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'server') expected_server_href = self.SERVER_HREF expected_server_bookmark = self.SERVER_BOOKMARK @@ -3613,48 +3982,57 @@ class ServerXMLSerializationTest(test.TestCase): expected_flavor_bookmark = self.FLAVOR_BOOKMARK expected_now = self.TIMESTAMP expected_uuid = FAKE_UUID - expected = minidom.parseString(""" - <server id="1" - uuid="%(expected_uuid)s" - xmlns="http://docs.openstack.org/compute/api/v1.1" - xmlns:atom="http://www.w3.org/2005/Atom" - name="test_server" - updated="%(expected_now)s" - created="%(expected_now)s" - hostId="e4d909c290d0fb1ca068ffaddf22cbd0" - status="BUILD" - adminPass="test_password" - progress="0"> - <atom:link href="%(expected_server_href)s" rel="self"/> - <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> - <image id="5"> - <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> - </image> - <flavor id="1"> - <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> - </flavor> - <metadata> - <meta key="Open"> - Stack - </meta> - <meta key="Number"> - 1 - </meta> - </metadata> - <addresses> - <network id="network_one"> - <ip version="4" addr="67.23.10.138"/> - <ip version="6" addr="::babe:67.23.10.138"/> - </network> - <network id="network_two"> - <ip version="4" addr="67.23.10.139"/> - <ip version="6" addr="::babe:67.23.10.139"/> - </network> - </addresses> - </server> - """.replace(" ", "") % (locals())) - - self.assertEqual(expected.toxml(), actual.toxml()) + server_dict = fixture['server'] + + for key in ['name', 'id', 'uuid', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6', 'adminPass']: + self.assertEqual(root.get(key), str(server_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 2) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + image_root = root.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = root.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = root.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) def test_index(self): serializer = servers.ServerXMLSerializer() @@ -3695,23 +4073,21 @@ class ServerXMLSerializationTest(test.TestCase): ]} output = serializer.serialize(fixture, 'index') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - <servers xmlns="http://docs.openstack.org/compute/api/v1.1" - xmlns:atom="http://www.w3.org/2005/Atom"> - <server id="1" name="test_server"> - <atom:link href="%(expected_server_href)s" rel="self"/> - <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> - </server> - <server id="2" name="test_server_2"> - <atom:link href="%(expected_server_href_2)s" rel="self"/> - <atom:link href="%(expected_server_bookmark_2)s" rel="bookmark"/> - </server> - </servers> - """.replace(" ", "") % (locals())) - - self.assertEqual(expected.toxml(), actual.toxml()) + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'servers_index') + server_elems = root.findall('{0}server'.format(NS)) + self.assertEqual(len(server_elems), 2) + for i, server_elem in enumerate(server_elems): + server_dict = fixture['servers'][i] + for key in ['name', 'id']: + self.assertEqual(server_elem.get(key), str(server_dict[key])) + + link_nodes = server_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) def test_detail(self): serializer = servers.ServerXMLSerializer() @@ -3734,6 +4110,8 @@ class ServerXMLSerializationTest(test.TestCase): "progress": 0, "name": "test_server", "status": "BUILD", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', "image": { "id": "5", @@ -3787,6 +4165,8 @@ class ServerXMLSerializationTest(test.TestCase): "progress": 100, "name": "test_server_2", "status": "ACTIVE", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', "image": { "id": "5", @@ -3835,71 +4215,63 @@ class ServerXMLSerializationTest(test.TestCase): ]} output = serializer.serialize(fixture, 'detail') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - <servers xmlns="http://docs.openstack.org/compute/api/v1.1" - xmlns:atom="http://www.w3.org/2005/Atom"> - <server id="1" - uuid="%(expected_uuid)s" - name="test_server" - updated="%(expected_now)s" - created="%(expected_now)s" - hostId="e4d909c290d0fb1ca068ffaddf22cbd0" - status="BUILD" - progress="0"> - <atom:link href="%(expected_server_href)s" rel="self"/> - <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> - <image id="5"> - <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> - </image> - <flavor id="1"> - <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> - </flavor> - <metadata> - <meta key="Number"> - 1 - </meta> - </metadata> - <addresses> - <network id="network_one"> - <ip version="4" addr="67.23.10.138"/> - <ip version="6" addr="::babe:67.23.10.138"/> - </network> - </addresses> - </server> - <server id="2" - uuid="%(expected_uuid)s" - name="test_server_2" - updated="%(expected_now)s" - created="%(expected_now)s" - hostId="e4d909c290d0fb1ca068ffaddf22cbd0" - status="ACTIVE" - progress="100"> - <atom:link href="%(expected_server_href_2)s" rel="self"/> - <atom:link href="%(expected_server_bookmark_2)s" rel="bookmark"/> - <image id="5"> - <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> - </image> - <flavor id="1"> - <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> - </flavor> - <metadata> - <meta key="Number"> - 2 - </meta> - </metadata> - <addresses> - <network id="network_one"> - <ip version="4" addr="67.23.10.138"/> - <ip version="6" addr="::babe:67.23.10.138"/> - </network> - </addresses> - </server> - </servers> - """.replace(" ", "") % (locals())) - - self.assertEqual(expected.toxml(), actual.toxml()) + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'servers') + server_elems = root.findall('{0}server'.format(NS)) + self.assertEqual(len(server_elems), 2) + for i, server_elem in enumerate(server_elems): + server_dict = fixture['servers'][i] + + for key in ['name', 'id', 'uuid', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6']: + self.assertEqual(server_elem.get(key), str(server_dict[key])) + + link_nodes = server_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = server_elem.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), + str(meta_value)) + + image_root = server_elem.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = server_elem.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), + server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = server_elem.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) def test_update(self): serializer = servers.ServerXMLSerializer() @@ -3914,6 +4286,8 @@ class ServerXMLSerializationTest(test.TestCase): "name": "test_server", "status": "BUILD", "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", "image": { "id": "5", "links": [ @@ -3972,7 +4346,9 @@ class ServerXMLSerializationTest(test.TestCase): } output = serializer.serialize(fixture, 'update') - actual = minidom.parseString(output.replace(" ", "")) + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'server') expected_server_href = self.SERVER_HREF expected_server_bookmark = self.SERVER_BOOKMARK @@ -3980,44 +4356,54 @@ class ServerXMLSerializationTest(test.TestCase): expected_flavor_bookmark = self.FLAVOR_BOOKMARK expected_now = self.TIMESTAMP expected_uuid = FAKE_UUID - expected = minidom.parseString(""" - <server id="1" - uuid="%(expected_uuid)s" - xmlns="http://docs.openstack.org/compute/api/v1.1" - xmlns:atom="http://www.w3.org/2005/Atom" - name="test_server" - updated="%(expected_now)s" - created="%(expected_now)s" - hostId="e4d909c290d0fb1ca068ffaddf22cbd0" - status="BUILD" - progress="0"> - <atom:link href="%(expected_server_href)s" rel="self"/> - <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> - <image id="5"> - <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> - </image> - <flavor id="1"> - <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> - </flavor> - <metadata> - <meta key="Open"> - Stack - </meta> - <meta key="Number"> - 1 - </meta> - </metadata> - <addresses> - <network id="network_one"> - <ip version="4" addr="67.23.10.138"/> - <ip version="6" addr="::babe:67.23.10.138"/> - </network> - <network id="network_two"> - <ip version="4" addr="67.23.10.139"/> - <ip version="6" addr="::babe:67.23.10.139"/> - </network> - </addresses> - </server> - """.replace(" ", "") % (locals())) - - self.assertEqual(expected.toxml(), actual.toxml()) + server_dict = fixture['server'] + + for key in ['name', 'id', 'uuid', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6']: + self.assertEqual(root.get(key), str(server_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 2) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + image_root = root.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = root.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = root.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py index 035a35aab..67c35fe6b 100644 --- a/nova/tests/integrated/api/client.py +++ b/nova/tests/integrated/api/client.py @@ -48,6 +48,14 @@ class OpenStackApiAuthenticationException(OpenStackApiException): response) +class OpenStackApiAuthorizationException(OpenStackApiException): + def __init__(self, response=None, message=None): + if not message: + message = _("Authorization error") + super(OpenStackApiAuthorizationException, self).__init__(message, + response) + + class OpenStackApiNotFoundException(OpenStackApiException): def __init__(self, response=None, message=None): if not message: @@ -69,6 +77,8 @@ class TestOpenStackClient(object): self.auth_user = auth_user self.auth_key = auth_key self.auth_uri = auth_uri + # default project_id + self.project_id = 'openstack' def request(self, url, method='GET', body=None, headers=None): _headers = {'Content-Type': 'application/json'} @@ -105,7 +115,8 @@ class TestOpenStackClient(object): auth_uri = self.auth_uri headers = {'X-Auth-User': self.auth_user, - 'X-Auth-Key': self.auth_key} + 'X-Auth-Key': self.auth_key, + 'X-Auth-Project-Id': self.project_id} response = self.request(auth_uri, headers=headers) @@ -127,7 +138,8 @@ class TestOpenStackClient(object): # NOTE(justinsb): httplib 'helpfully' converts headers to lower case base_uri = auth_result['x-server-management-url'] - full_uri = base_uri + relative_uri + + full_uri = '%s/%s' % (base_uri, relative_uri) headers = kwargs.setdefault('headers', {}) headers['X-Auth-Token'] = auth_result['x-auth-token'] @@ -141,6 +153,8 @@ class TestOpenStackClient(object): if not http_status in check_response_status: if http_status == 404: raise OpenStackApiNotFoundException(response=response) + elif http_status == 401: + raise OpenStackApiAuthorizationException(response=response) else: raise OpenStackApiException( message=_("Unexpected status code"), @@ -256,7 +270,8 @@ class TestOpenStackClient(object): def post_server_volume(self, server_id, volume_attachment): return self.api_post('/servers/%s/os-volume_attachments' % - (server_id), volume_attachment)['volumeAttachment'] + (server_id), volume_attachment + )['volumeAttachment'] def delete_server_volume(self, server_id, attachment_id): return self.api_delete('/servers/%s/os-volume_attachments/%s' % diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py index 06359a52f..9d1925bc0 100644 --- a/nova/tests/integrated/test_login.py +++ b/nova/tests/integrated/test_login.py @@ -59,6 +59,12 @@ class LoginTest(integrated_helpers._IntegratedTestBase): self.assertRaises(client.OpenStackApiAuthenticationException, bad_credentials_api.get_flavors) + def test_good_login_bad_project(self): + """Test that I get a 401 with valid user/pass but bad project""" + self.api.project_id = 'openstackBAD' + + self.assertRaises(client.OpenStackApiAuthorizationException, + self.api.get_flavors) if __name__ == "__main__": unittest.main() diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 4f5d36f14..993a87f23 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -347,7 +347,7 @@ class ComputeTestCase(test.TestCase): self.assertEquals(msg['priority'], 'INFO') self.assertEquals(msg['event_type'], 'compute.instance.create') payload = msg['payload'] - self.assertEquals(payload['tenant_id'], self.project_id) + self.assertEquals(payload['project_id'], self.project_id) self.assertEquals(payload['user_id'], self.user_id) self.assertEquals(payload['instance_id'], instance_id) self.assertEquals(payload['instance_type'], 'm1.tiny') @@ -371,7 +371,7 @@ class ComputeTestCase(test.TestCase): self.assertEquals(msg['priority'], 'INFO') self.assertEquals(msg['event_type'], 'compute.instance.delete') payload = msg['payload'] - self.assertEquals(payload['tenant_id'], self.project_id) + self.assertEquals(payload['project_id'], self.project_id) self.assertEquals(payload['user_id'], self.user_id) self.assertEquals(payload['instance_id'], instance_id) self.assertEquals(payload['instance_type'], 'm1.tiny') @@ -454,7 +454,7 @@ class ComputeTestCase(test.TestCase): self.assertEquals(msg['priority'], 'INFO') self.assertEquals(msg['event_type'], 'compute.instance.resize.prep') payload = msg['payload'] - self.assertEquals(payload['tenant_id'], self.project_id) + self.assertEquals(payload['project_id'], self.project_id) self.assertEquals(payload['user_id'], self.user_id) self.assertEquals(payload['instance_id'], instance_id) self.assertEquals(payload['instance_type'], 'm1.tiny') diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 91130de67..6b34a90f9 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -31,6 +31,8 @@ sys.dont_write_bytecode = True import imp nova_manage = imp.load_source('nova_manage.py', NOVA_MANAGE_PATH) sys.dont_write_bytecode = False +import mox +import stubout import netaddr import StringIO @@ -40,6 +42,7 @@ from nova import exception from nova import flags from nova import log as logging from nova import test +from nova.tests.db import fakes as db_fakes FLAGS = flags.FLAGS LOG = logging.getLogger('nova.tests.nova_manage') @@ -48,44 +51,24 @@ LOG = logging.getLogger('nova.tests.nova_manage') class FixedIpCommandsTestCase(test.TestCase): def setUp(self): super(FixedIpCommandsTestCase, self).setUp() - cidr = '10.0.0.0/24' - net = netaddr.IPNetwork(cidr) - net_info = {'bridge': 'fakebr', - 'bridge_interface': 'fakeeth', - 'dns': FLAGS.flat_network_dns, - 'cidr': cidr, - 'netmask': str(net.netmask), - 'gateway': str(net[1]), - 'broadcast': str(net.broadcast), - 'dhcp_start': str(net[2])} - self.network = db.network_create_safe(context.get_admin_context(), - net_info) - num_ips = len(net) - for index in range(num_ips): - address = str(net[index]) - reserved = (index == 1 or index == 2) - db.fixed_ip_create(context.get_admin_context(), - {'network_id': self.network['id'], - 'address': address, - 'reserved': reserved}) + self.stubs = stubout.StubOutForTesting() + db_fakes.stub_out_db_network_api(self.stubs) self.commands = nova_manage.FixedIpCommands() def tearDown(self): - db.network_delete_safe(context.get_admin_context(), self.network['id']) super(FixedIpCommandsTestCase, self).tearDown() + self.stubs.UnsetAll() def test_reserve(self): - self.commands.reserve('10.0.0.100') + self.commands.reserve('192.168.0.100') address = db.fixed_ip_get_by_address(context.get_admin_context(), - '10.0.0.100') + '192.168.0.100') self.assertEqual(address['reserved'], True) def test_unreserve(self): - db.fixed_ip_update(context.get_admin_context(), '10.0.0.100', - {'reserved': True}) - self.commands.unreserve('10.0.0.100') + self.commands.unreserve('192.168.0.100') address = db.fixed_ip_get_by_address(context.get_admin_context(), - '10.0.0.100') + '192.168.0.100') self.assertEqual(address['reserved'], False) diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index ec5098a37..28e366a8e 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -384,3 +384,13 @@ class ToPrimitiveTestCase(test.TestCase): def test_typeerror(self): x = bytearray # Class, not instance self.assertEquals(utils.to_primitive(x), u"<type 'bytearray'>") + + def test_nasties(self): + def foo(): + pass + x = [datetime, foo, dir] + ret = utils.to_primitive(x) + self.assertEquals(len(ret), 3) + self.assertTrue(ret[0].startswith(u"<module 'datetime' from ")) + self.assertTrue(ret[1].startswith(u'<function foo at 0x')) + self.assertEquals(ret[2], u'<built-in function dir>') diff --git a/nova/utils.py b/nova/utils.py index 08821ebee..fc4bbd53b 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -295,7 +295,7 @@ EASIER_PASSWORD_SYMBOLS = ('23456789' # Removed: 0, 1 def usage_from_instance(instance_ref, **kw): usage_info = dict( - tenant_id=instance_ref['project_id'], + project_id=instance_ref['project_id'], user_id=instance_ref['user_id'], instance_id=instance_ref['id'], instance_type=instance_ref['instance_type']['name'], @@ -547,11 +547,17 @@ def to_primitive(value, convert_instances=False, level=0): Therefore, convert_instances=True is lossy ... be aware. """ - if inspect.isclass(value): - return unicode(value) + nasty = [inspect.ismodule, inspect.isclass, inspect.ismethod, + inspect.isfunction, inspect.isgeneratorfunction, + inspect.isgenerator, inspect.istraceback, inspect.isframe, + inspect.iscode, inspect.isbuiltin, inspect.isroutine, + inspect.isabstract] + for test in nasty: + if test(value): + return unicode(value) if level > 3: - return [] + return '?' # The try block may not be necessary after the class check above, # but just in case ... |
