Merged from trunk,resolved conflicts and fixed broken unit tests due to changes in the extensions which now include ProjectMapper

This commit is contained in:
Tushar Patil 2011-08-22 16:35:09 -07:00
commit 4ded14d0d8
48 changed files with 1912 additions and 717 deletions

View File

@ -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']})

View File

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

View File

@ -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,56 +100,19 @@ 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)
if 'fixed_ip' in ip:
self.disassociate(req, id)
self.network_api.release_floating_ip(context, address=ip['address'])
return {'released': {
"id": ip['id'],
"floating_ip": ip['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']
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 """
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
if 'fixed_ip' in floating_ip:
self.network_api.disassociate_floating_ip(context,
floating_ip['address'])
return {'disassociated': {'floating_ip': address,
'fixed_ip': fixed_ip}}
self.network_api.release_floating_ip(context,
address=floating_ip['address'])
return webob.exc.HTTPAccepted()
def _get_ip_by_id(self, context, value):
"""Checks that value is id and then returns its address."""
@ -152,6 +120,47 @@ class FloatingIPController(object):
class Floating_ips(extensions.ExtensionDescriptor):
def __init__(self):
self.compute_api = compute.API()
self.network_api = network.API()
super(Floating_ips, self).__init__()
def _add_floating_ip(self, input_dict, req, instance_id):
"""Associate floating_ip to an instance."""
context = req.environ['nova.context']
try:
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)
self.compute_api.associate_floating_ip(context, instance_id, 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)
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

View File

@ -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 = []

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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']))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1514,6 +1514,19 @@ def instance_add_security_group(context, instance_id, security_group_id):
instance_ref.save(session=session)
@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."""
@ -2484,6 +2497,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).\
@ -2491,6 +2505,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(

View File

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

View File

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

View File

@ -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.")

View File

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

View File

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

View File

@ -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
@ -29,6 +30,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)
resp = req.get_response(fakes.wsgi_app())
self.assertEqual(resp.status_int, 202)
def test_floating_ip_disassociate(self):
body = dict()
req = webob.Request.blank('/v1.1/os-floating-ips/1/disassociate')
req.method = 'POST'
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -28,55 +28,35 @@ 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
from nova import context
from nova import db
from nova import flags
from nova import exception
from nova import test
FLAGS = flags.FLAGS
from nova.tests.db import fakes as db_fakes
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)

View File

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

View File

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