summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorIsaku Yamahata <yamahata@valinux.co.jp>2011-08-02 11:36:58 +0900
committerIsaku Yamahata <yamahata@valinux.co.jp>2011-08-02 11:36:58 +0900
commit0ffc3b80490c24ee116f5be070249dcbbed9357b (patch)
tree74b5a542119899a9b483288d2fa6a354751d4090 /nova
parenta52b643b18e1bac18b642ecfd781809eb5612763 (diff)
parentbdcfaa5b927a096f507fb0f7e2d81989173957f8 (diff)
merged with nova trunk
Diffstat (limited to 'nova')
-rw-r--r--nova/api/direct.py3
-rw-r--r--nova/api/ec2/__init__.py32
-rw-r--r--nova/api/ec2/cloud.py142
-rw-r--r--nova/api/openstack/__init__.py10
-rw-r--r--nova/api/openstack/auth.py28
-rw-r--r--nova/api/openstack/common.py107
-rw-r--r--nova/api/openstack/contrib/volumes.py53
-rw-r--r--nova/api/openstack/create_instance_helper.py103
-rw-r--r--nova/api/openstack/extensions.py51
-rw-r--r--nova/api/openstack/faults.py17
-rw-r--r--nova/api/openstack/image_metadata.py71
-rw-r--r--nova/api/openstack/images.py2
-rw-r--r--nova/api/openstack/ips.py56
-rw-r--r--nova/api/openstack/servers.py203
-rw-r--r--nova/api/openstack/views/servers.py46
-rw-r--r--nova/api/openstack/wsgi.py42
-rw-r--r--nova/auth/dbdriver.py2
-rw-r--r--nova/auth/manager.py14
-rw-r--r--nova/cloudpipe/pipelib.py18
-rw-r--r--nova/compute/api.py14
-rw-r--r--nova/compute/instance_types.py9
-rw-r--r--nova/compute/manager.py81
-rw-r--r--nova/context.py90
-rw-r--r--nova/db/sqlalchemy/api.py18
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py9
-rw-r--r--nova/db/sqlalchemy/models.py16
-rw-r--r--nova/exception.py7
-rw-r--r--nova/flags.py2
-rw-r--r--nova/image/s3.py14
-rw-r--r--nova/log.py5
-rw-r--r--nova/network/api.py7
-rw-r--r--nova/network/linux_net.py1
-rw-r--r--nova/network/manager.py85
-rw-r--r--nova/network/vmwareapi_net.py82
-rw-r--r--nova/network/xenapi_net.py87
-rw-r--r--nova/rpc/__init__.py66
-rw-r--r--nova/rpc/amqp.py (renamed from nova/rpc.py)23
-rw-r--r--nova/rpc/common.py23
-rw-r--r--nova/scheduler/least_cost.py1
-rw-r--r--nova/scheduler/zone_aware_scheduler.py2
-rw-r--r--nova/service.py28
-rw-r--r--nova/test.py16
-rw-r--r--nova/tests/api/openstack/contrib/test_floating_ips.py5
-rw-r--r--nova/tests/api/openstack/contrib/test_multinic_xs.py8
-rw-r--r--nova/tests/api/openstack/fakes.py40
-rw-r--r--nova/tests/api/openstack/test_accounts.py10
-rw-r--r--nova/tests/api/openstack/test_adminapi.py17
-rw-r--r--nova/tests/api/openstack/test_auth.py42
-rw-r--r--nova/tests/api/openstack/test_common.py219
-rw-r--r--nova/tests/api/openstack/test_extensions.py237
-rw-r--r--nova/tests/api/openstack/test_faults.py107
-rw-r--r--nova/tests/api/openstack/test_flavors.py5
-rw-r--r--nova/tests/api/openstack/test_flavors_extra_specs.py (renamed from nova/tests/api/openstack/extensions/test_flavors_extra_specs.py)66
-rw-r--r--nova/tests/api/openstack/test_image_metadata.py249
-rw-r--r--nova/tests/api/openstack/test_images.py29
-rw-r--r--nova/tests/api/openstack/test_limits.py14
-rw-r--r--nova/tests/api/openstack/test_server_metadata.py14
-rw-r--r--nova/tests/api/openstack/test_servers.py1651
-rw-r--r--nova/tests/api/openstack/test_shared_ip_groups.py13
-rw-r--r--nova/tests/api/openstack/test_users.py9
-rw-r--r--nova/tests/api/openstack/test_wsgi.py7
-rw-r--r--nova/tests/api/openstack/test_zones.py25
-rw-r--r--nova/tests/fake_flags.py1
-rw-r--r--nova/tests/hyperv_unittest.py9
-rw-r--r--nova/tests/integrated/test_servers.py1
-rw-r--r--nova/tests/scheduler/test_scheduler.py22
-rw-r--r--nova/tests/scheduler/test_zone_aware_scheduler.py18
-rw-r--r--nova/tests/test_access.py21
-rw-r--r--nova/tests/test_adminapi.py18
-rw-r--r--nova/tests/test_api.py82
-rw-r--r--nova/tests/test_auth.py19
-rw-r--r--nova/tests/test_cloud.py111
-rw-r--r--nova/tests/test_compute.py60
-rw-r--r--nova/tests/test_console.py19
-rw-r--r--nova/tests/test_db_api.py78
-rw-r--r--nova/tests/test_libvirt.py240
-rw-r--r--nova/tests/test_network.py9
-rw-r--r--nova/tests/test_objectstore.py27
-rw-r--r--nova/tests/test_quota.py65
-rw-r--r--nova/tests/test_rpc.py61
-rw-r--r--nova/tests/test_rpc_amqp.py68
-rw-r--r--nova/tests/test_service.py170
-rw-r--r--nova/tests/test_test.py13
-rw-r--r--nova/tests/test_vmwareapi.py108
-rw-r--r--nova/tests/test_xenapi.py226
-rw-r--r--nova/tests/vmwareapi/db_fakes.py10
-rw-r--r--nova/tests/vmwareapi/stubs.py7
-rw-r--r--nova/utils.py23
-rw-r--r--nova/virt/driver.py19
-rw-r--r--nova/virt/fake.py10
-rw-r--r--nova/virt/hyperv.py10
-rw-r--r--nova/virt/images.py2
-rw-r--r--nova/virt/libvirt.xml.template24
-rw-r--r--nova/virt/libvirt/connection.py117
-rw-r--r--nova/virt/libvirt/firewall.py11
-rw-r--r--nova/virt/libvirt/netutils.py10
-rw-r--r--nova/virt/libvirt/vif.py134
-rw-r--r--nova/virt/vif.py30
-rw-r--r--nova/virt/vmwareapi/fake.py10
-rw-r--r--nova/virt/vmwareapi/network_utils.py29
-rw-r--r--nova/virt/vmwareapi/vif.py95
-rw-r--r--nova/virt/vmwareapi/vm_util.py33
-rw-r--r--nova/virt/vmwareapi/vmops.py48
-rw-r--r--nova/virt/vmwareapi_conn.py16
-rw-r--r--nova/virt/xenapi/vif.py140
-rw-r--r--nova/virt/xenapi/vm_utils.py73
-rw-r--r--nova/virt/xenapi/vmops.py88
-rw-r--r--nova/virt/xenapi_conn.py32
-rw-r--r--nova/volume/api.py2
-rw-r--r--nova/wsgi.py12
110 files changed, 4665 insertions, 2219 deletions
diff --git a/nova/api/direct.py b/nova/api/direct.py
index ec79151b1..993815fc7 100644
--- a/nova/api/direct.py
+++ b/nova/api/direct.py
@@ -107,7 +107,8 @@ class DelegatedAuthMiddleware(wsgi.Middleware):
def process_request(self, request):
os_user = request.headers['X-OpenStack-User']
os_project = request.headers['X-OpenStack-Project']
- context_ref = context.RequestContext(user=os_user, project=os_project)
+ context_ref = context.RequestContext(user_id=os_user,
+ project_id=os_project)
request.environ['openstack.context'] = context_ref
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index cf1734281..af232edda 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -66,7 +66,7 @@ class RequestLogging(wsgi.Middleware):
else:
controller = None
action = None
- ctxt = request.environ.get('ec2.context', None)
+ ctxt = request.environ.get('nova.context', None)
delta = utils.utcnow() - start
seconds = delta.seconds
microseconds = delta.microseconds
@@ -139,8 +139,7 @@ class Lockout(wsgi.Middleware):
class Authenticate(wsgi.Middleware):
-
- """Authenticate an EC2 request and add 'ec2.context' to WSGI environ."""
+ """Authenticate an EC2 request and add 'nova.context' to WSGI environ."""
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
@@ -157,8 +156,9 @@ class Authenticate(wsgi.Middleware):
auth_params.pop('Signature')
# Authenticate the request.
+ authman = manager.AuthManager()
try:
- (user, project) = manager.AuthManager().authenticate(
+ (user, project) = authman.authenticate(
access,
signature,
auth_params,
@@ -174,14 +174,17 @@ class Authenticate(wsgi.Middleware):
remote_address = req.remote_addr
if FLAGS.use_forwarded_for:
remote_address = req.headers.get('X-Forwarded-For', remote_address)
- ctxt = context.RequestContext(user=user,
- project=project,
+ roles = authman.get_active_roles(user, project)
+ ctxt = context.RequestContext(user_id=user.id,
+ project_id=project.id,
+ is_admin=user.is_admin(),
+ roles=roles,
remote_address=remote_address)
- req.environ['ec2.context'] = ctxt
+ req.environ['nova.context'] = ctxt
uname = user.name
pname = project.name
msg = _('Authenticated Request For %(uname)s:%(pname)s)') % locals()
- LOG.audit(msg, context=req.environ['ec2.context'])
+ LOG.audit(msg, context=req.environ['nova.context'])
return self.application
@@ -228,7 +231,7 @@ class Authorizer(wsgi.Middleware):
"""Authorize an EC2 API request.
Return a 401 if ec2.controller and ec2.action in WSGI environ may not be
- executed in ec2.context.
+ executed in nova.context.
"""
def __init__(self, application):
@@ -282,7 +285,7 @@ class Authorizer(wsgi.Middleware):
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
- context = req.environ['ec2.context']
+ context = req.environ['nova.context']
controller = req.environ['ec2.request'].controller.__class__.__name__
action = req.environ['ec2.request'].action
allowed_roles = self.action_roles[controller].get(action, ['none'])
@@ -295,28 +298,27 @@ class Authorizer(wsgi.Middleware):
def _matches_any_role(self, context, roles):
"""Return True if any role in roles is allowed in context."""
- if context.user.is_superuser():
+ if context.is_admin:
return True
if 'all' in roles:
return True
if 'none' in roles:
return False
- return any(context.project.has_role(context.user_id, role)
- for role in roles)
+ return any(role in context.roles for role in roles)
class Executor(wsgi.Application):
"""Execute an EC2 API request.
- Executes 'ec2.action' upon 'ec2.controller', passing 'ec2.context' and
+ Executes 'ec2.action' upon 'ec2.controller', passing 'nova.context' and
'ec2.action_args' (all variables in WSGI environ.) Returns an XML
response, or a 400 upon failure.
"""
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
- context = req.environ['ec2.context']
+ context = req.environ['nova.context']
api_request = req.environ['ec2.request']
result = None
try:
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 9b0ec2fde..c4651f374 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -554,7 +554,55 @@ class CloudController(object):
g['ipPermissions'] += [r]
return g
- def _revoke_rule_args_to_dict(self, context, to_port=None, from_port=None,
+ def _rule_args_to_dict(self, context, kwargs):
+ rules = []
+ if not 'groups' in kwargs and not 'ip_ranges' in kwargs:
+ rule = self._rule_dict_last_step(context, **kwargs)
+ if rule:
+ rules.append(rule)
+ return rules
+ if 'ip_ranges' in kwargs:
+ rules = self._cidr_args_split(kwargs)
+ else:
+ rules = [kwargs]
+ finalset = []
+ for rule in rules:
+ if 'groups' in rule:
+ groups_values = self._groups_args_split(rule)
+ for groups_value in groups_values:
+ final = self._rule_dict_last_step(context, **groups_value)
+ finalset.append(final)
+ else:
+ final = self._rule_dict_last_step(context, **rule)
+ finalset.append(final)
+ return finalset
+
+ def _cidr_args_split(self, kwargs):
+ cidr_args_split = []
+ cidrs = kwargs['ip_ranges']
+ for key, cidr in cidrs.iteritems():
+ mykwargs = kwargs.copy()
+ del mykwargs['ip_ranges']
+ mykwargs['cidr_ip'] = cidr['cidr_ip']
+ cidr_args_split.append(mykwargs)
+ return cidr_args_split
+
+ def _groups_args_split(self, kwargs):
+ groups_args_split = []
+ groups = kwargs['groups']
+ for key, group in groups.iteritems():
+ mykwargs = kwargs.copy()
+ del mykwargs['groups']
+ if 'group_name' in group:
+ mykwargs['source_security_group_name'] = group['group_name']
+ if 'user_id' in group:
+ mykwargs['source_security_group_owner_id'] = group['user_id']
+ if 'group_id' in group:
+ mykwargs['source_security_group_id'] = group['group_id']
+ groups_args_split.append(mykwargs)
+ return groups_args_split
+
+ def _rule_dict_last_step(self, context, to_port=None, from_port=None,
ip_protocol=None, cidr_ip=None, user_id=None,
source_security_group_name=None,
source_security_group_owner_id=None):
@@ -569,6 +617,9 @@ class CloudController(object):
db.security_group_get_by_name(context.elevated(),
source_project_id,
source_security_group_name)
+ notfound = exception.SecurityGroupNotFound
+ if not source_security_group:
+ raise notfound(security_group_id=source_security_group_name)
values['group_id'] = source_security_group['id']
elif cidr_ip:
# If this fails, it throws an exception. This is what we want.
@@ -607,7 +658,7 @@ class CloudController(object):
for rule in security_group.rules:
if 'group_id' in values:
if rule['group_id'] == values['group_id']:
- return True
+ return rule['id']
else:
is_duplicate = True
for key in ('cidr', 'from_port', 'to_port', 'protocol'):
@@ -615,7 +666,7 @@ class CloudController(object):
is_duplicate = False
break
if is_duplicate:
- return True
+ return rule['id']
return False
def revoke_security_group_ingress(self, context, group_name=None,
@@ -638,22 +689,30 @@ class CloudController(object):
msg = "Revoke security group ingress %s"
LOG.audit(_(msg), security_group['name'], context=context)
-
- criteria = self._revoke_rule_args_to_dict(context, **kwargs)
- if criteria is None:
- raise exception.ApiError(_("Not enough parameters to build a "
- "valid rule."))
-
- for rule in security_group.rules:
- match = True
- for (k, v) in criteria.iteritems():
- if getattr(rule, k, False) != v:
- match = False
- if match:
- db.security_group_rule_destroy(context, rule['id'])
- self.compute_api.trigger_security_group_rules_refresh(context,
- security_group_id=security_group['id'])
- return True
+ prevalues = []
+ try:
+ prevalues = kwargs['ip_permissions']
+ except KeyError:
+ prevalues.append(kwargs)
+ rule_id = None
+ for values in prevalues:
+ rulesvalues = self._rule_args_to_dict(context, values)
+ if not rulesvalues:
+ err = "%s Not enough parameters to build a valid rule"
+ raise exception.ApiError(_(err % rulesvalues))
+
+ for values_for_rule in rulesvalues:
+ values_for_rule['parent_group_id'] = security_group.id
+ rule_id = self._security_group_rule_exists(security_group,
+ values_for_rule)
+ if rule_id:
+ db.security_group_rule_destroy(context, rule_id)
+ if rule_id:
+ # NOTE(vish): we removed a rule, so refresh
+ self.compute_api.trigger_security_group_rules_refresh(
+ context,
+ security_group_id=security_group['id'])
+ return True
raise exception.ApiError(_("No rule for the specified parameters."))
# TODO(soren): This has only been tested with Boto as the client.
@@ -680,22 +739,37 @@ class CloudController(object):
msg = "Authorize security group ingress %s"
LOG.audit(_(msg), security_group['name'], context=context)
- values = self._revoke_rule_args_to_dict(context, **kwargs)
- if values is None:
- raise exception.ApiError(_("Not enough parameters to build a "
- "valid rule."))
- values['parent_group_id'] = security_group.id
-
- if self._security_group_rule_exists(security_group, values):
- raise exception.ApiError(_('This rule already exists in group %s')
- % group_name)
-
- security_group_rule = db.security_group_rule_create(context, values)
+ prevalues = []
+ try:
+ prevalues = kwargs['ip_permissions']
+ except KeyError:
+ prevalues.append(kwargs)
+ postvalues = []
+ for values in prevalues:
+ rulesvalues = self._rule_args_to_dict(context, values)
+ if not rulesvalues:
+ err = "%s Not enough parameters to build a valid rule"
+ raise exception.ApiError(_(err % rulesvalues))
+ for values_for_rule in rulesvalues:
+ values_for_rule['parent_group_id'] = security_group.id
+ if self._security_group_rule_exists(security_group,
+ values_for_rule):
+ err = '%s - This rule already exists in group'
+ raise exception.ApiError(_(err) % values_for_rule)
+ postvalues.append(values_for_rule)
+
+ for values_for_rule in postvalues:
+ security_group_rule = db.security_group_rule_create(
+ context,
+ values_for_rule)
- self.compute_api.trigger_security_group_rules_refresh(context,
- security_group_id=security_group['id'])
+ if postvalues:
+ self.compute_api.trigger_security_group_rules_refresh(
+ context,
+ security_group_id=security_group['id'])
+ return True
- return True
+ raise exception.ApiError(_("No rule for the specified parameters."))
def _get_source_project_id(self, context, source_security_group_owner_id):
if source_security_group_owner_id:
@@ -1274,7 +1348,7 @@ class CloudController(object):
def rescue_instance(self, context, instance_id, **kwargs):
"""This is an extension to the normal ec2_api"""
- self._do_instance(self.compute_api.rescue, contect, instnace_id)
+ self._do_instance(self.compute_api.rescue, context, instance_id)
return True
def unrescue_instance(self, context, instance_id, **kwargs):
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index e87d7c754..868b98a31 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -164,11 +164,17 @@ class APIRouterV11(APIRouter):
def _setup_routes(self, mapper):
super(APIRouterV11, self)._setup_routes(mapper, '1.1')
- mapper.resource("image_meta", "meta",
- controller=image_metadata.create_resource(),
+ image_metadata_controller = image_metadata.create_resource()
+ mapper.resource("image_meta", "metadata",
+ controller=image_metadata_controller,
parent_resource=dict(member_name='image',
collection_name='images'))
+ mapper.connect("metadata", "/images/{image_id}/metadata",
+ controller=image_metadata_controller,
+ action='update_all',
+ conditions={"method": ['PUT']})
+
mapper.resource("server_meta", "meta",
controller=server_metadata.create_resource(),
parent_resource=dict(member_name='server',
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 7c3e683d6..d42abe1f8 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -48,31 +48,35 @@ class AuthMiddleware(wsgi.Middleware):
def __call__(self, req):
if not self.has_authentication(req):
return self.authenticate(req)
- user = self.get_user_by_authentication(req)
- if not user:
+ user_id = self.get_user_by_authentication(req)
+ if not user_id:
token = req.headers["X-Auth-Token"]
- msg = _("%(user)s could not be found with token '%(token)s'")
+ msg = _("%(user_id)s could not be found with token '%(token)s'")
LOG.warn(msg % locals())
return faults.Fault(webob.exc.HTTPUnauthorized())
try:
- account = req.headers["X-Auth-Project-Id"]
+ 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
- accounts = self.auth.get_projects(user=user)
- if accounts:
- account = accounts[0]
+ projects = self.auth.get_projects(user_id)
+ if projects:
+ project_id = projects[0].id
else:
return faults.Fault(webob.exc.HTTPUnauthorized())
- if not self.auth.is_admin(user) and \
- not self.auth.is_project_member(user, account):
- msg = _("%(user)s must be an admin or a member of %(account)s")
+ is_admin = self.auth.is_admin(user_id)
+ req.environ['nova.context'] = context.RequestContext(user_id,
+ project_id,
+ is_admin)
+ if not is_admin and not self.auth.is_project_member(user_id,
+ project_id):
+ msg = _("%(user_id)s must be an admin or a "
+ "member of %(project_id)s")
LOG.warn(msg % locals())
return faults.Fault(webob.exc.HTTPUnauthorized())
- req.environ['nova.context'] = context.RequestContext(user, account)
return self.application
def has_authentication(self, req):
@@ -133,7 +137,7 @@ class AuthMiddleware(wsgi.Middleware):
if delta.days >= 2:
self.db.auth_token_destroy(ctxt, token['token_hash'])
else:
- return self.auth.get_user(token['user_id'])
+ return token['user_id']
return None
def _authorize_user(self, username, key, req):
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 57031ebf1..efa4ab385 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -17,12 +17,14 @@
import re
from urlparse import urlparse
+from xml.dom import minidom
import webob
from nova import exception
from nova import flags
from nova import log as logging
+from nova.api.openstack import wsgi
LOG = logging.getLogger('nova.api.openstack.common')
@@ -167,3 +169,108 @@ def remove_version_from_href(href):
msg = _('href does not contain version')
raise ValueError(msg)
return new_href
+
+
+def get_version_from_href(href):
+ """Returns the api version in the href.
+
+ Returns the api version in the href.
+ If no version is found, 1.0 is returned
+
+ Given: 'http://www.nova.com/123'
+ Returns: '1.0'
+
+ Given: 'http://www.nova.com/v1.1'
+ Returns: '1.1'
+
+ """
+ try:
+ #finds the first instance that matches /v#.#/
+ version = re.findall(r'[/][v][0-9]+\.[0-9]+[/]', href)
+ #if no version was found, try finding /v#.# at the end of the string
+ if not version:
+ version = re.findall(r'[/][v][0-9]+\.[0-9]+$', href)
+ version = re.findall(r'[0-9]+\.[0-9]', version[0])[0]
+ except IndexError:
+ version = '1.0'
+ return version
+
+
+class MetadataXMLDeserializer(wsgi.MetadataXMLDeserializer):
+
+ def _extract_metadata_container(self, datastring):
+ dom = minidom.parseString(datastring)
+ metadata_node = self.find_first_child_named(dom, "metadata")
+ metadata = self.extract_metadata(metadata_node)
+ return {'body': {'metadata': metadata}}
+
+ def create(self, datastring):
+ return self._extract_metadata_container(datastring)
+
+ def update_all(self, datastring):
+ return self._extract_metadata_container(datastring)
+
+ def update(self, datastring):
+ dom = minidom.parseString(datastring)
+ metadata_item = self.extract_metadata(dom)
+ return {'body': {'meta': metadata_item}}
+
+
+class MetadataHeadersSerializer(wsgi.ResponseHeadersSerializer):
+
+ def delete(self, response, data):
+ response.status_int = 204
+
+
+class MetadataXMLSerializer(wsgi.XMLDictSerializer):
+ def __init__(self, xmlns=wsgi.XMLNS_V11):
+ super(MetadataXMLSerializer, self).__init__(xmlns=xmlns)
+
+ def _meta_item_to_xml(self, doc, key, value):
+ node = doc.createElement('meta')
+ doc.appendChild(node)
+ node.setAttribute('key', '%s' % key)
+ text = doc.createTextNode('%s' % value)
+ node.appendChild(text)
+ return node
+
+ def meta_list_to_xml(self, xml_doc, meta_items):
+ container_node = xml_doc.createElement('metadata')
+ for (key, value) in meta_items:
+ item_node = self._meta_item_to_xml(xml_doc, key, value)
+ container_node.appendChild(item_node)
+ return container_node
+
+ def _meta_list_to_xml_string(self, metadata_dict):
+ xml_doc = minidom.Document()
+ items = metadata_dict['metadata'].items()
+ container_node = self.meta_list_to_xml(xml_doc, items)
+ xml_doc.appendChild(container_node)
+ self._add_xmlns(container_node)
+ return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
+
+ def index(self, metadata_dict):
+ return self._meta_list_to_xml_string(metadata_dict)
+
+ def create(self, metadata_dict):
+ return self._meta_list_to_xml_string(metadata_dict)
+
+ def update_all(self, metadata_dict):
+ return self._meta_list_to_xml_string(metadata_dict)
+
+ def _meta_item_to_xml_string(self, meta_item_dict):
+ xml_doc = minidom.Document()
+ item_key, item_value = meta_item_dict.items()[0]
+ item_node = self._meta_item_to_xml(xml_doc, item_key, item_value)
+ xml_doc.appendChild(item_node)
+ self._add_xmlns(item_node)
+ return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
+
+ def show(self, meta_item_dict):
+ return self._meta_item_to_xml_string(meta_item_dict['meta'])
+
+ def update(self, meta_item_dict):
+ return self._meta_item_to_xml_string(meta_item_dict['meta'])
+
+ def default(self, *args, **kwargs):
+ return ''
diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py
index 827e36097..867fe301e 100644
--- a/nova/api/openstack/contrib/volumes.py
+++ b/nova/api/openstack/contrib/volumes.py
@@ -22,10 +22,12 @@ from nova import compute
from nova import exception
from nova import flags
from nova import log as logging
+from nova import quota
from nova import volume
from nova.api.openstack import common
from nova.api.openstack import extensions
from nova.api.openstack import faults
+from nova.api.openstack import servers
LOG = logging.getLogger("nova.api.volumes")
@@ -297,6 +299,53 @@ class VolumeAttachmentController(object):
return {'volumeAttachments': res}
+class BootFromVolumeController(servers.ControllerV11):
+ """The boot from volume API controller for the Openstack API."""
+
+ def _create_instance(self, context, instance_type, image_href, **kwargs):
+ try:
+ return self.compute_api.create(context, instance_type,
+ image_href, **kwargs)
+ except quota.QuotaError as error:
+ self.helper._handle_quota_error(error)
+ except exception.ImageNotFound as error:
+ msg = _("Can not find requested image")
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ def create(self, req, body):
+ """ Creates a new server for a given user """
+ extra_values = None
+ try:
+
+ def get_kwargs(context, instance_type, image_href, **kwargs):
+ kwargs['context'] = context
+ kwargs['instance_type'] = instance_type
+ kwargs['image_href'] = image_href
+ return kwargs
+
+ extra_values, kwargs = self.helper.create_instance(req, body,
+ get_kwargs)
+
+ block_device_mapping = body['server'].get('block_device_mapping')
+ kwargs['block_device_mapping'] = block_device_mapping
+
+ instances = self._create_instance(**kwargs)
+ except faults.Fault, f:
+ return f
+
+ # We can only return 1 instance via the API, if we happen to
+ # build more than one... instances is a list, so we'll just
+ # use the first one..
+ inst = instances[0]
+ for key in ['instance_type', 'image_ref']:
+ inst[key] = extra_values[key]
+
+ builder = self._get_view_builder(req)
+ server = builder.build(inst, is_detail=True)
+ server['server']['adminPass'] = extra_values['password']
+ return server
+
+
class Volumes(extensions.ExtensionDescriptor):
def get_name(self):
return "Volumes"
@@ -330,4 +379,8 @@ class Volumes(extensions.ExtensionDescriptor):
collection_name='servers'))
resources.append(res)
+ res = extensions.ResourceExtension('os-volumes_boot',
+ BootFromVolumeController())
+ resources.append(res)
+
return resources
diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py
index 7249f1261..9199c193d 100644
--- a/nova/api/openstack/create_instance_helper.py
+++ b/nova/api/openstack/create_instance_helper.py
@@ -20,6 +20,7 @@ import webob
from webob import exc
from xml.dom import minidom
+from nova import db
from nova import exception
from nova import flags
from nova import log as logging
@@ -29,7 +30,6 @@ from nova import utils
from nova.compute import instance_types
from nova.api.openstack import wsgi
-from nova.auth import manager as auth_manager
LOG = logging.getLogger('nova.api.openstack.create_instance_helper')
@@ -71,13 +71,19 @@ class CreateInstanceHelper(object):
if not body:
raise exc.HTTPUnprocessableEntity()
- context = req.environ['nova.context']
+ if not 'server' in body:
+ raise exc.HTTPUnprocessableEntity()
- password = self.controller._get_server_admin_password(body['server'])
+ server_dict = body['server']
+ context = req.environ['nova.context']
+ password = self.controller._get_server_admin_password(server_dict)
key_name = None
key_data = None
- key_pairs = auth_manager.AuthManager.get_key_pairs(context)
+ # TODO(vish): Key pair access should move into a common library
+ # instead of being accessed directly from the db.
+ key_pairs = db.key_pair_get_all_by_user(context.elevated(),
+ context.user_id)
if key_pairs:
key_pair = key_pairs[0]
key_name = key_pair['name']
@@ -95,7 +101,7 @@ class CreateInstanceHelper(object):
locals())
raise exc.HTTPBadRequest(explanation=msg)
- personality = body['server'].get('personality')
+ personality = server_dict.get('personality')
injected_files = []
if personality:
@@ -107,18 +113,18 @@ class CreateInstanceHelper(object):
msg = _("Invalid flavorRef provided.")
raise exc.HTTPBadRequest(explanation=msg)
- if not 'name' in body['server']:
+ if not 'name' in server_dict:
msg = _("Server name is not defined")
raise exc.HTTPBadRequest(explanation=msg)
- zone_blob = body['server'].get('blob')
- name = body['server']['name']
+ zone_blob = server_dict.get('blob')
+ name = server_dict['name']
self._validate_server_name(name)
name = name.strip()
- reservation_id = body['server'].get('reservation_id')
- min_count = body['server'].get('min_count')
- max_count = body['server'].get('max_count')
+ reservation_id = server_dict.get('reservation_id')
+ min_count = server_dict.get('min_count')
+ max_count = server_dict.get('max_count')
# min_count and max_count are optional. If they exist, they come
# in as strings. We want to default 'min_count' to 1, and default
# 'max_count' to be 'min_count'.
@@ -145,7 +151,7 @@ class CreateInstanceHelper(object):
display_description=name,
key_name=key_name,
key_data=key_data,
- metadata=body['server'].get('metadata', {}),
+ metadata=server_dict.get('metadata', {}),
injected_files=injected_files,
admin_password=password,
zone_blob=zone_blob,
@@ -185,7 +191,7 @@ class CreateInstanceHelper(object):
Overrides normal behavior in the case of xml content
"""
if request.content_type == "application/xml":
- deserializer = ServerCreateRequestXMLDeserializer()
+ deserializer = ServerXMLDeserializer()
return deserializer.deserialize(request.body)
else:
return self._deserialize(request.body, request.get_content_type())
@@ -282,7 +288,7 @@ class CreateInstanceHelper(object):
return password
-class ServerXMLDeserializer(wsgi.XMLDeserializer):
+class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer):
"""
Deserializer to handle xml-formatted server create requests.
@@ -299,61 +305,30 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer):
def _extract_server(self, node):
"""Marshal the server attribute of a parsed request"""
server = {}
- server_node = self._find_first_child_named(node, 'server')
- for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]:
+ server_node = self.find_first_child_named(node, 'server')
+
+ attributes = ["name", "imageId", "flavorId", "imageRef",
+ "flavorRef", "adminPass"]
+ for attr in attributes:
if server_node.getAttribute(attr):
server[attr] = server_node.getAttribute(attr)
- metadata = self._extract_metadata(server_node)
- if metadata is not None:
- server["metadata"] = metadata
- personality = self._extract_personality(server_node)
- if personality is not None:
- server["personality"] = personality
- return server
- def _extract_metadata(self, server_node):
- """Marshal the metadata attribute of a parsed request"""
- metadata_node = self._find_first_child_named(server_node, "metadata")
- if metadata_node is None:
- return None
- metadata = {}
- for meta_node in self._find_children_named(metadata_node, "meta"):
- key = meta_node.getAttribute("key")
- metadata[key] = self._extract_text(meta_node)
- return metadata
+ metadata_node = self.find_first_child_named(server_node, "metadata")
+ server["metadata"] = self.extract_metadata(metadata_node)
+
+ server["personality"] = self._extract_personality(server_node)
+
+ return server
def _extract_personality(self, server_node):
"""Marshal the personality attribute of a parsed request"""
- personality_node = \
- self._find_first_child_named(server_node, "personality")
- if personality_node is None:
- return None
+ node = self.find_first_child_named(server_node, "personality")
personality = []
- for file_node in self._find_children_named(personality_node, "file"):
- item = {}
- if file_node.hasAttribute("path"):
- item["path"] = file_node.getAttribute("path")
- item["contents"] = self._extract_text(file_node)
- personality.append(item)
+ if node is not None:
+ for file_node in self.find_children_named(node, "file"):
+ item = {}
+ if file_node.hasAttribute("path"):
+ item["path"] = file_node.getAttribute("path")
+ item["contents"] = self.extract_text(file_node)
+ personality.append(item)
return personality
-
- def _find_first_child_named(self, parent, name):
- """Search a nodes children for the first child with a given name"""
- for node in parent.childNodes:
- if node.nodeName == name:
- return node
- return None
-
- def _find_children_named(self, parent, name):
- """Return all of a nodes children who have the given name"""
- for node in parent.childNodes:
- if node.nodeName == name:
- yield node
-
- def _extract_text(self, node):
- """Get the text field contained by the given node"""
- if len(node.childNodes) == 1:
- child = node.childNodes[0]
- if child.nodeType == child.TEXT_NODE:
- return child.nodeValue
- return ""
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index da06ecd15..cc889703e 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -23,6 +23,7 @@ import sys
import routes
import webob.dec
import webob.exc
+from xml.etree import ElementTree
from nova import exception
from nova import flags
@@ -194,7 +195,7 @@ class ExtensionsResource(wsgi.Resource):
def show(self, req, id):
# NOTE(dprince): the extensions alias is used as the 'id' for show
ext = self.extension_manager.extensions[id]
- return self._translate(ext)
+ return dict(extension=self._translate(ext))
def delete(self, req, id):
raise faults.Fault(webob.exc.HTTPNotFound())
@@ -258,15 +259,18 @@ class ExtensionMiddleware(base_wsgi.Middleware):
mapper = routes.Mapper()
+ serializer = wsgi.ResponseSerializer(
+ {'application/xml': ExtensionsXMLSerializer()})
# extended resources
for resource in ext_mgr.get_resources():
LOG.debug(_('Extended resource: %s'),
resource.collection)
mapper.resource(resource.collection, resource.collection,
- controller=wsgi.Resource(resource.controller),
- collection=resource.collection_actions,
- member=resource.member_actions,
- parent_resource=resource.parent)
+ controller=wsgi.Resource(
+ resource.controller, serializer=serializer),
+ collection=resource.collection_actions,
+ member=resource.member_actions,
+ parent_resource=resource.parent)
# extended actions
action_resources = self._action_ext_resources(application, ext_mgr,
@@ -462,3 +466,40 @@ class ResourceExtension(object):
self.parent = parent
self.collection_actions = collection_actions
self.member_actions = member_actions
+
+
+class ExtensionsXMLSerializer(wsgi.XMLDictSerializer):
+
+ def show(self, ext_dict):
+ ext = self._create_ext_elem(ext_dict['extension'])
+ return self._to_xml(ext)
+
+ def index(self, exts_dict):
+ exts = ElementTree.Element('extensions')
+ for ext_dict in exts_dict['extensions']:
+ exts.append(self._create_ext_elem(ext_dict))
+ return self._to_xml(exts)
+
+ def _create_ext_elem(self, ext_dict):
+ """Create an extension xml element from a dict."""
+ ext_elem = ElementTree.Element('extension')
+ ext_elem.set('name', ext_dict['name'])
+ ext_elem.set('namespace', ext_dict['namespace'])
+ ext_elem.set('alias', ext_dict['alias'])
+ ext_elem.set('updated', ext_dict['updated'])
+ desc = ElementTree.Element('description')
+ desc.text = ext_dict['description']
+ ext_elem.append(desc)
+ for link in ext_dict.get('links', []):
+ elem = ElementTree.Element('atom:link')
+ elem.set('rel', link['rel'])
+ elem.set('href', link['href'])
+ elem.set('type', link['type'])
+ ext_elem.append(elem)
+ return ext_elem
+
+ def _to_xml(self, root):
+ """Convert the xml tree object to an xml string."""
+ root.set('xmlns', wsgi.XMLNS_V11)
+ root.set('xmlns:atom', wsgi.XMLNS_ATOM)
+ return ElementTree.tostring(root, encoding='UTF-8')
diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py
index 24cde69e4..1ab45d4f1 100644
--- a/nova/api/openstack/faults.py
+++ b/nova/api/openstack/faults.py
@@ -19,6 +19,7 @@
import webob.dec
import webob.exc
+from nova.api.openstack import common
from nova.api.openstack import wsgi
@@ -61,9 +62,13 @@ class Fault(webob.exc.HTTPException):
content_type = req.best_match_content_type()
+ xml_serializer = {
+ '1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10),
+ '1.1': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V11),
+ }[common.get_version_from_href(req.url)]
+
serializer = {
- 'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
- xmlns=wsgi.XMLNS_V10),
+ 'application/xml': xml_serializer,
'application/json': wsgi.JSONDictSerializer(),
}[content_type]
@@ -100,9 +105,13 @@ class OverLimitFault(webob.exc.HTTPException):
content_type = request.best_match_content_type()
metadata = {"attributes": {"overLimitFault": "code"}}
+ xml_serializer = {
+ '1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10),
+ '1.1': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V11),
+ }[common.get_version_from_href(request.url)]
+
serializer = {
- 'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
- xmlns=wsgi.XMLNS_V10),
+ 'application/xml': xml_serializer,
'application/json': wsgi.JSONDictSerializer(),
}[content_type]
diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py
index c0fc8c09b..aaf64a123 100644
--- a/nova/api/openstack/image_metadata.py
+++ b/nova/api/openstack/image_metadata.py
@@ -16,12 +16,12 @@
# under the License.
from webob import exc
-from xml.dom import minidom
from nova import flags
from nova import image
from nova import quota
from nova import utils
+from nova.api.openstack import common
from nova.api.openstack import wsgi
@@ -96,8 +96,16 @@ class Controller(object):
self._check_quota_limit(context, metadata)
img['properties'] = metadata
self.image_service.update(context, image_id, img, None)
+ return dict(meta=meta)
- return req.body
+ def update_all(self, req, image_id, body):
+ context = req.environ['nova.context']
+ img = self.image_service.show(context, image_id)
+ metadata = body.get('metadata', {})
+ self._check_quota_limit(context, metadata)
+ img['properties'] = metadata
+ self.image_service.update(context, image_id, img, None)
+ return dict(metadata=metadata)
def delete(self, req, image_id, id):
context = req.environ['nova.context']
@@ -110,58 +118,17 @@ class Controller(object):
self.image_service.update(context, image_id, img, None)
-class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer):
- def __init__(self, xmlns=wsgi.XMLNS_V11):
- super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns)
-
- def _meta_item_to_xml(self, doc, key, value):
- node = doc.createElement('meta')
- doc.appendChild(node)
- node.setAttribute('key', '%s' % key)
- text = doc.createTextNode('%s' % value)
- node.appendChild(text)
- return node
-
- def meta_list_to_xml(self, xml_doc, meta_items):
- container_node = xml_doc.createElement('metadata')
- for (key, value) in meta_items:
- item_node = self._meta_item_to_xml(xml_doc, key, value)
- container_node.appendChild(item_node)
- return container_node
-
- def _meta_list_to_xml_string(self, metadata_dict):
- xml_doc = minidom.Document()
- items = metadata_dict['metadata'].items()
- container_node = self.meta_list_to_xml(xml_doc, items)
- xml_doc.appendChild(container_node)
- self._add_xmlns(container_node)
- return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
-
- def index(self, metadata_dict):
- return self._meta_list_to_xml_string(metadata_dict)
-
- def create(self, metadata_dict):
- return self._meta_list_to_xml_string(metadata_dict)
-
- def _meta_item_to_xml_string(self, meta_item_dict):
- xml_doc = minidom.Document()
- item_key, item_value = meta_item_dict.items()[0]
- item_node = self._meta_item_to_xml(xml_doc, item_key, item_value)
- xml_doc.appendChild(item_node)
- self._add_xmlns(item_node)
- return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
-
- def show(self, meta_item_dict):
- return self._meta_item_to_xml_string(meta_item_dict['meta'])
-
- def update(self, meta_item_dict):
- return self._meta_item_to_xml_string(meta_item_dict['meta'])
+def create_resource():
+ headers_serializer = common.MetadataHeadersSerializer()
+ body_deserializers = {
+ 'application/xml': common.MetadataXMLDeserializer(),
+ }
-def create_resource():
body_serializers = {
- 'application/xml': ImageMetadataXMLSerializer(),
+ 'application/xml': common.MetadataXMLSerializer(),
}
- serializer = wsgi.ResponseSerializer(body_serializers)
+ serializer = wsgi.ResponseSerializer(body_serializers, headers_serializer)
+ deserializer = wsgi.RequestDeserializer(body_deserializers)
- return wsgi.Resource(Controller(), serializer=serializer)
+ return wsgi.Resource(Controller(), deserializer, serializer)
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index 30e4fd389..9ba8b639e 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -284,7 +284,7 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer):
xmlns = wsgi.XMLNS_V11
def __init__(self):
- self.metadata_serializer = image_metadata.ImageMetadataXMLSerializer()
+ self.metadata_serializer = common.MetadataXMLSerializer()
def _image_to_xml(self, xml_doc, image):
image_node = xml_doc.createElement('image')
diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py
index 2996b032d..a74fae487 100644
--- a/nova/api/openstack/ips.py
+++ b/nova/api/openstack/ips.py
@@ -16,6 +16,7 @@
# under the License.
import time
+from xml.dom import minidom
from webob import exc
@@ -100,17 +101,51 @@ class ControllerV11(Controller):
return nova.api.openstack.views.addresses.ViewBuilderV11()
+class IPXMLSerializer(wsgi.XMLDictSerializer):
+ def __init__(self, xmlns=wsgi.XMLNS_V11):
+ super(IPXMLSerializer, self).__init__(xmlns=xmlns)
+
+ def _ip_to_xml(self, xml_doc, ip_dict):
+ ip_node = xml_doc.createElement('ip')
+ ip_node.setAttribute('addr', ip_dict['addr'])
+ ip_node.setAttribute('version', str(ip_dict['version']))
+ return ip_node
+
+ def _network_to_xml(self, xml_doc, network_id, ip_dicts):
+ network_node = xml_doc.createElement('network')
+ network_node.setAttribute('id', network_id)
+
+ for ip_dict in ip_dicts:
+ ip_node = self._ip_to_xml(xml_doc, ip_dict)
+ network_node.appendChild(ip_node)
+
+ return network_node
+
+ def networks_to_xml(self, xml_doc, networks_container):
+ addresses_node = xml_doc.createElement('addresses')
+ for (network_id, ip_dicts) in networks_container.items():
+ network_node = self._network_to_xml(xml_doc, network_id, ip_dicts)
+ addresses_node.appendChild(network_node)
+ return addresses_node
+
+ def show(self, network_container):
+ (network_id, ip_dicts) = network_container.items()[0]
+ xml_doc = minidom.Document()
+ node = self._network_to_xml(xml_doc, network_id, ip_dicts)
+ return self.to_xml_string(node, False)
+
+ def index(self, addresses_container):
+ xml_doc = minidom.Document()
+ node = self.networks_to_xml(xml_doc, addresses_container['addresses'])
+ return self.to_xml_string(node, False)
+
+
def create_resource(version):
controller = {
'1.0': ControllerV10,
'1.1': ControllerV11,
}[version]()
- xmlns = {
- '1.0': wsgi.XMLNS_V10,
- '1.1': wsgi.XMLNS_V11,
- }[version]
-
metadata = {
'list_collections': {
'public': {'item_name': 'ip', 'item_key': 'addr'},
@@ -118,10 +153,11 @@ def create_resource(version):
},
}
- body_serializers = {
- 'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
- xmlns=xmlns),
- }
- serializer = wsgi.ResponseSerializer(body_serializers)
+ xml_serializer = {
+ '1.0': wsgi.XMLDictSerializer(metadata=metadata, xmlns=wsgi.XMLNS_V11),
+ '1.1': IPXMLSerializer(),
+ }[version]
+
+ serializer = wsgi.ResponseSerializer({'application/xml': xml_serializer})
return wsgi.Resource(controller, serializer=serializer)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 7bef1d9b2..30169d450 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -17,23 +17,25 @@ import base64
import traceback
from webob import exc
+from xml.dom import minidom
import webob
from nova import compute
-from nova import db
from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
from nova.api.openstack import common
from nova.api.openstack import create_instance_helper as helper
+from nova.api.openstack import ips
+from nova.api.openstack import wsgi
+from nova.compute import instance_types
+from nova.scheduler import api as scheduler_api
+import nova.api.openstack
import nova.api.openstack.views.addresses
import nova.api.openstack.views.flavors
import nova.api.openstack.views.images
import nova.api.openstack.views.servers
-from nova.api.openstack import wsgi
-import nova.api.openstack
-from nova.scheduler import api as scheduler_api
LOG = logging.getLogger('nova.api.openstack.servers')
@@ -436,18 +438,25 @@ class ControllerV10(Controller):
def _action_resize(self, input_dict, req, id):
""" Resizes a given instance to the flavor size requested """
- if 'resize' in input_dict and 'flavorId' in input_dict['resize']:
- flavor_id = input_dict['resize']['flavorId']
- self.compute_api.resize(req.environ['nova.context'], id,
- flavor_id)
- else:
- LOG.exception(_("Missing 'flavorId' argument for resize"))
- raise exc.HTTPUnprocessableEntity()
+ try:
+ flavor_id = input_dict["resize"]["flavorId"]
+ except (KeyError, TypeError):
+ msg = _("Resize requests require 'flavorId' attribute.")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ try:
+ i_type = instance_types.get_instance_type_by_flavor_id(flavor_id)
+ except exception.FlavorNotFound:
+ msg = _("Unable to locate requested flavor.")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ context = req.environ["nova.context"]
+ self.compute_api.resize(context, id, i_type["id"])
+
return webob.Response(status_int=202)
def _action_rebuild(self, info, request, instance_id):
context = request.environ['nova.context']
- instance_id = int(instance_id)
try:
image_id = info["rebuild"]["imageId"]
@@ -459,7 +468,7 @@ class ControllerV10(Controller):
try:
self.compute_api.rebuild(context, instance_id, image_id)
except exception.BuildInProgress:
- msg = _("Instance %d is currently being rebuilt.") % instance_id
+ msg = _("Instance %s is currently being rebuilt.") % instance_id
LOG.debug(msg)
raise exc.HTTPConflict(explanation=msg)
@@ -481,11 +490,20 @@ class ControllerV11(Controller):
raise exc.HTTPNotFound()
def _image_ref_from_req_data(self, data):
- return data['server']['imageRef']
+ try:
+ return data['server']['imageRef']
+ except (TypeError, KeyError):
+ msg = _("Missing imageRef attribute")
+ raise exc.HTTPBadRequest(explanation=msg)
def _flavor_id_from_req_data(self, data):
- href = data['server']['flavorRef']
- return common.get_id_from_href(href)
+ try:
+ flavor_ref = data['server']['flavorRef']
+ except (TypeError, KeyError):
+ msg = _("Missing flavorRef attribute")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ return common.get_id_from_href(flavor_ref)
def _build_view(self, req, instance, is_detail=False):
base_url = req.application_url
@@ -545,22 +563,24 @@ class ControllerV11(Controller):
def _action_resize(self, input_dict, req, id):
""" Resizes a given instance to the flavor size requested """
try:
- if 'resize' in input_dict and 'flavorRef' in input_dict['resize']:
- flavor_ref = input_dict['resize']['flavorRef']
- flavor_id = common.get_id_from_href(flavor_ref)
- self.compute_api.resize(req.environ['nova.context'], id,
- flavor_id)
- else:
- LOG.exception(_("Missing 'flavorRef' argument for resize"))
- raise exc.HTTPUnprocessableEntity()
- except Exception, e:
- LOG.exception(_("Error in resize %s"), e)
- raise exc.HTTPBadRequest()
+ flavor_ref = input_dict["resize"]["flavorRef"]
+ except (KeyError, TypeError):
+ msg = _("Resize requests require 'flavorRef' attribute.")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ try:
+ i_type = instance_types.get_instance_type_by_flavor_id(flavor_ref)
+ except exception.FlavorNotFound:
+ msg = _("Unable to locate requested flavor.")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ context = req.environ["nova.context"]
+ self.compute_api.resize(context, id, i_type["id"])
+
return webob.Response(status_int=202)
def _action_rebuild(self, info, request, instance_id):
context = request.environ['nova.context']
- instance_id = int(instance_id)
try:
image_href = info["rebuild"]["imageRef"]
@@ -581,7 +601,7 @@ class ControllerV11(Controller):
self.compute_api.rebuild(context, instance_id, image_href, name,
metadata, personalities)
except exception.BuildInProgress:
- msg = _("Instance %d is currently being rebuilt.") % instance_id
+ msg = _("Instance %s is currently being rebuilt.") % instance_id
LOG.debug(msg)
raise exc.HTTPConflict(explanation=msg)
@@ -601,6 +621,123 @@ class HeadersSerializer(wsgi.ResponseHeadersSerializer):
response.status_int = 204
+class ServerXMLSerializer(wsgi.XMLDictSerializer):
+
+ xmlns = wsgi.XMLNS_V11
+
+ def __init__(self):
+ self.metadata_serializer = common.MetadataXMLSerializer()
+ self.addresses_serializer = ips.IPXMLSerializer()
+
+ def _create_basic_entity_node(self, xml_doc, id, links, name):
+ basic_node = xml_doc.createElement(name)
+ basic_node.setAttribute('id', str(id))
+ link_nodes = self._create_link_nodes(xml_doc, links)
+ for link_node in link_nodes:
+ basic_node.appendChild(link_node)
+ return basic_node
+
+ def _create_metadata_node(self, xml_doc, metadata):
+ return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata)
+
+ def _create_addresses_node(self, xml_doc, addresses):
+ return self.addresses_serializer.networks_to_xml(xml_doc, addresses)
+
+ def _add_server_attributes(self, node, server):
+ node.setAttribute('id', str(server['id']))
+ node.setAttribute('uuid', str(server['uuid']))
+ node.setAttribute('hostId', str(server['hostId']))
+ node.setAttribute('name', server['name'])
+ node.setAttribute('created', str(server['created']))
+ node.setAttribute('updated', str(server['updated']))
+ node.setAttribute('status', server['status'])
+ if 'progress' in server:
+ node.setAttribute('progress', str(server['progress']))
+
+ def _server_to_xml(self, xml_doc, server):
+ server_node = xml_doc.createElement('server')
+ server_node.setAttribute('id', str(server['id']))
+ server_node.setAttribute('name', server['name'])
+ link_nodes = self._create_link_nodes(xml_doc,
+ server['links'])
+ for link_node in link_nodes:
+ server_node.appendChild(link_node)
+ return server_node
+
+ def _server_to_xml_detailed(self, xml_doc, server):
+ server_node = xml_doc.createElement('server')
+ self._add_server_attributes(server_node, server)
+
+ link_nodes = self._create_link_nodes(xml_doc,
+ server['links'])
+ for link_node in link_nodes:
+ server_node.appendChild(link_node)
+
+ if 'image' in server:
+ image_node = self._create_basic_entity_node(xml_doc,
+ server['image']['id'],
+ server['image']['links'],
+ 'image')
+ server_node.appendChild(image_node)
+
+ if 'flavor' in server:
+ flavor_node = self._create_basic_entity_node(xml_doc,
+ server['flavor']['id'],
+ server['flavor']['links'],
+ 'flavor')
+ server_node.appendChild(flavor_node)
+
+ metadata = server.get('metadata', {}).items()
+ if len(metadata) > 0:
+ metadata_node = self._create_metadata_node(xml_doc, metadata)
+ server_node.appendChild(metadata_node)
+
+ addresses_node = self._create_addresses_node(xml_doc,
+ server['addresses'])
+ server_node.appendChild(addresses_node)
+
+ return server_node
+
+ def _server_list_to_xml(self, xml_doc, servers, detailed):
+ container_node = xml_doc.createElement('servers')
+ if detailed:
+ server_to_xml = self._server_to_xml_detailed
+ else:
+ server_to_xml = self._server_to_xml
+
+ for server in servers:
+ item_node = server_to_xml(xml_doc, server)
+ container_node.appendChild(item_node)
+ return container_node
+
+ def index(self, servers_dict):
+ xml_doc = minidom.Document()
+ node = self._server_list_to_xml(xml_doc,
+ servers_dict['servers'],
+ detailed=False)
+ return self.to_xml_string(node, True)
+
+ def detail(self, servers_dict):
+ xml_doc = minidom.Document()
+ node = self._server_list_to_xml(xml_doc,
+ servers_dict['servers'],
+ detailed=True)
+ return self.to_xml_string(node, True)
+
+ def show(self, server_dict):
+ xml_doc = minidom.Document()
+ node = self._server_to_xml_detailed(xml_doc,
+ server_dict['server'])
+ return self.to_xml_string(node, True)
+
+ def create(self, server_dict):
+ xml_doc = minidom.Document()
+ node = self._server_to_xml_detailed(xml_doc,
+ server_dict['server'])
+ node.setAttribute('adminPass', server_dict['server']['adminPass'])
+ return self.to_xml_string(node, True)
+
+
def create_resource(version='1.0'):
controller = {
'1.0': ControllerV10,
@@ -630,9 +767,13 @@ def create_resource(version='1.0'):
headers_serializer = HeadersSerializer()
+ xml_serializer = {
+ '1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10),
+ '1.1': ServerXMLSerializer(),
+ }[version]
+
body_serializers = {
- 'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
- xmlns=xmlns),
+ 'application/xml': xml_serializer,
}
body_deserializers = {
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 7131db088..2873a8e0f 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import datetime
import hashlib
import os
@@ -50,7 +51,7 @@ class ViewBuilder(object):
else:
server = self._build_simple(inst)
- self._build_extra(server, inst)
+ self._build_extra(server['server'], inst)
return server
@@ -99,7 +100,6 @@ class ViewBuilder(object):
self._build_flavor(inst_dict, inst)
self._build_addresses(inst_dict, inst)
- inst_dict['uuid'] = inst['uuid']
return dict(server=inst_dict)
def _build_addresses(self, response, inst):
@@ -121,6 +121,9 @@ class ViewBuilder(object):
class ViewBuilderV10(ViewBuilder):
"""Model an Openstack API V1.0 server response."""
+ def _build_extra(self, response, inst):
+ response['uuid'] = inst['uuid']
+
def _build_image(self, response, inst):
if 'image_ref' in dict(inst):
image_ref = inst['image_ref']
@@ -145,18 +148,46 @@ class ViewBuilderV11(ViewBuilder):
self.image_builder = image_builder
self.base_url = base_url
+ def _build_detail(self, inst):
+ response = super(ViewBuilderV11, self)._build_detail(inst)
+ response['server']['created'] = utils.isotime(inst['created_at'])
+ response['server']['updated'] = utils.isotime(inst['updated_at'])
+ if 'status' in response['server']:
+ if response['server']['status'] == "ACTIVE":
+ response['server']['progress'] = 100
+ elif response['server']['status'] == "BUILD":
+ response['server']['progress'] = 0
+ return response
+
def _build_image(self, response, inst):
if 'image_ref' in dict(inst):
image_href = inst['image_ref']
- if str(image_href).isdigit():
- image_href = int(image_href)
- response['imageRef'] = image_href
+ image_id = str(common.get_id_from_href(image_href))
+ _bookmark = self.image_builder.generate_bookmark(image_id)
+ response['image'] = {
+ "id": image_id,
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": _bookmark,
+ },
+ ]
+ }
def _build_flavor(self, response, inst):
if "instance_type" in dict(inst):
flavor_id = inst["instance_type"]['flavorid']
flavor_ref = self.flavor_builder.generate_href(flavor_id)
- response["flavorRef"] = flavor_ref
+ flavor_bookmark = self.flavor_builder.generate_bookmark(flavor_id)
+ response["flavor"] = {
+ "id": str(common.get_id_from_href(flavor_ref)),
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ]
+ }
def _build_addresses(self, response, inst):
interfaces = inst.get('virtual_interfaces', [])
@@ -164,6 +195,7 @@ class ViewBuilderV11(ViewBuilder):
def _build_extra(self, response, inst):
self._build_links(response, inst)
+ response['uuid'] = inst['uuid']
def _build_links(self, response, inst):
href = self.generate_href(inst["id"])
@@ -180,7 +212,7 @@ class ViewBuilderV11(ViewBuilder):
},
]
- response["server"]["links"] = links
+ response["links"] = links
def generate_href(self, server_id):
"""Create an url that refers to a specific server id."""
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index 1e5a5143d..53dab22e8 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -13,6 +13,7 @@ from nova import wsgi
XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
+XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
LOG = logging.getLogger('nova.api.openstack.wsgi')
@@ -135,10 +136,43 @@ class XMLDeserializer(TextDeserializer):
listnames)
return result
+ def find_first_child_named(self, parent, name):
+ """Search a nodes children for the first child with a given name"""
+ for node in parent.childNodes:
+ if node.nodeName == name:
+ return node
+ return None
+
+ def find_children_named(self, parent, name):
+ """Return all of a nodes children who have the given name"""
+ for node in parent.childNodes:
+ if node.nodeName == name:
+ yield node
+
+ def extract_text(self, node):
+ """Get the text field contained by the given node"""
+ if len(node.childNodes) == 1:
+ child = node.childNodes[0]
+ if child.nodeType == child.TEXT_NODE:
+ return child.nodeValue
+ return ""
+
def default(self, datastring):
return {'body': self._from_xml(datastring)}
+class MetadataXMLDeserializer(XMLDeserializer):
+
+ def extract_metadata(self, metadata_node):
+ """Marshal the metadata attribute of a parsed request"""
+ metadata = {}
+ if metadata_node is not None:
+ for meta_node in self.find_children_named(metadata_node, "meta"):
+ key = meta_node.getAttribute("key")
+ metadata[key] = self.extract_text(meta_node)
+ return metadata
+
+
class RequestHeadersDeserializer(ActionDispatcher):
"""Default request headers deserializer"""
@@ -396,8 +430,9 @@ class ResponseSerializer(object):
def serialize_body(self, response, data, content_type, action):
response.headers['Content-Type'] = content_type
- serializer = self.get_body_serializer(content_type)
- response.body = serializer.serialize(data, action)
+ if data is not None:
+ serializer = self.get_body_serializer(content_type)
+ response.body = serializer.serialize(data, action)
def get_body_serializer(self, content_type):
try:
@@ -443,7 +478,7 @@ class Resource(wsgi.Application):
action, args, accept = self.deserializer.deserialize(request)
except exception.InvalidContentType:
msg = _("Unsupported Content-Type")
- return webob.exc.HTTPBadRequest(explanation=msg)
+ return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg))
except exception.MalformedRequestBody:
msg = _("Malformed request body")
return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg))
@@ -454,7 +489,6 @@ class Resource(wsgi.Application):
LOG.info(_("HTTP exception thrown: %s"), unicode(ex))
action_result = faults.Fault(ex)
- #TODO(bcwaldon): find a more elegant way to pass through non-dict types
if type(action_result) is dict or action_result is None:
response = self.serializer.serialize(action_result,
accept,
diff --git a/nova/auth/dbdriver.py b/nova/auth/dbdriver.py
index a429b7812..c6d81ee04 100644
--- a/nova/auth/dbdriver.py
+++ b/nova/auth/dbdriver.py
@@ -127,7 +127,7 @@ class DbDriver(object):
try:
project = db.project_create(context.get_admin_context(), values)
- except exception.Duplicate:
+ except exception.DBError:
raise exception.ProjectExists(project=name)
for member in members:
diff --git a/nova/auth/manager.py b/nova/auth/manager.py
index b6131fb7f..6205cfb56 100644
--- a/nova/auth/manager.py
+++ b/nova/auth/manager.py
@@ -518,6 +518,14 @@ class AuthManager(object):
return drv.get_user_roles(User.safe_id(user),
Project.safe_id(project))
+ def get_active_roles(self, user, project=None):
+ """Get all active roles for context"""
+ if project:
+ roles = FLAGS.allowed_roles + ['projectmanager']
+ else:
+ roles = FLAGS.global_roles
+ return [role for role in roles if self.has_role(user, role, project)]
+
def get_project(self, pid):
"""Get project object by id"""
with self.driver() as drv:
@@ -730,10 +738,6 @@ class AuthManager(object):
with self.driver() as drv:
drv.modify_user(uid, access_key, secret_key, admin)
- @staticmethod
- def get_key_pairs(context):
- return db.key_pair_get_all_by_user(context.elevated(), context.user_id)
-
def get_credentials(self, user, project=None, use_dmz=True):
"""Get credential zip for user in project"""
if not isinstance(user, User):
@@ -785,7 +789,7 @@ class AuthManager(object):
return read_buffer
def get_environment_rc(self, user, project=None, use_dmz=True):
- """Get credential zip for user in project"""
+ """Get environment rc for user in project"""
if not isinstance(user, User):
user = self.get_user(user)
if project is None:
diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py
index 7844d31e1..521525205 100644
--- a/nova/cloudpipe/pipelib.py
+++ b/nova/cloudpipe/pipelib.py
@@ -96,8 +96,8 @@ class CloudPipe(object):
def launch_vpn_instance(self, project_id):
LOG.debug(_("Launching VPN for %s") % (project_id))
project = self.manager.get_project(project_id)
- ctxt = context.RequestContext(user=project.project_manager,
- project=project)
+ ctxt = context.RequestContext(user=project.project_manager_id,
+ project=project.id)
key_name = self.setup_key_pair(ctxt)
group_name = self.setup_security_group(ctxt)
@@ -112,11 +112,11 @@ class CloudPipe(object):
security_group=[group_name])
def setup_security_group(self, context):
- group_name = '%s%s' % (context.project.id, FLAGS.vpn_key_suffix)
- if db.security_group_exists(context, context.project.id, group_name):
+ group_name = '%s%s' % (context.project_id, FLAGS.vpn_key_suffix)
+ if db.security_group_exists(context, context.project_id, group_name):
return group_name
- group = {'user_id': context.user.id,
- 'project_id': context.project.id,
+ group = {'user_id': context.user_id,
+ 'project_id': context.project_id,
'name': group_name,
'description': 'Group for vpn'}
group_ref = db.security_group_create(context, group)
@@ -137,12 +137,12 @@ class CloudPipe(object):
return group_name
def setup_key_pair(self, context):
- key_name = '%s%s' % (context.project.id, FLAGS.vpn_key_suffix)
+ key_name = '%s%s' % (context.project_id, FLAGS.vpn_key_suffix)
try:
- result = cloud._gen_key(context, context.user.id, key_name)
+ result = cloud._gen_key(context, context.user_id, key_name)
private_key = result['private_key']
try:
- key_dir = os.path.join(FLAGS.keys_path, context.user.id)
+ key_dir = os.path.join(FLAGS.keys_path, context.user_id)
if not os.path.exists(key_dir):
os.makedirs(key_dir)
key_path = os.path.join(key_dir, '%s.pem' % key_name)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 942114161..91c7e2026 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -127,7 +127,7 @@ class API(base.Base):
quota_metadata = quota.allowed_metadata_items(context, num_metadata)
if quota_metadata < num_metadata:
pid = context.project_id
- msg = _("Quota exceeeded for %(pid)s, tried to set "
+ msg = _("Quota exceeded for %(pid)s, tried to set "
"%(num_metadata)s metadata properties") % locals()
LOG.warn(msg)
raise quota.QuotaError(msg, "MetadataLimitExceeded")
@@ -138,7 +138,7 @@ class API(base.Base):
for k, v in metadata.iteritems():
if len(k) > 255 or len(v) > 255:
pid = context.project_id
- msg = _("Quota exceeeded for %(pid)s, metadata property "
+ msg = _("Quota exceeded for %(pid)s, metadata property "
"key or value too long") % locals()
LOG.warn(msg)
raise quota.QuotaError(msg, "MetadataLimitExceeded")
@@ -165,7 +165,7 @@ class API(base.Base):
instance_type)
if num_instances < min_count:
pid = context.project_id
- LOG.warn(_("Quota exceeeded for %(pid)s,"
+ LOG.warn(_("Quota exceeded for %(pid)s,"
" tried to run %(min_count)s instances") % locals())
if num_instances <= 0:
message = _("Instance quota exceeded. You cannot run any "
@@ -600,6 +600,7 @@ class API(base.Base):
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{'method': 'refresh_provider_fw_rules', 'args': {}})
+ @scheduler_api.reroute_compute("update")
def update(self, context, instance_id, **kwargs):
"""Updates the instance in the datastore.
@@ -727,7 +728,7 @@ class API(base.Base):
raise
instances = None
elif project_id or not context.is_admin:
- if not context.project:
+ if not context.project_id:
instances = self.db.instance_get_all_by_user(
context, context.user_id)
else:
@@ -815,6 +816,7 @@ class API(base.Base):
raise exception.Error(_("Unable to find host for Instance %s")
% instance_id)
+ @scheduler_api.reroute_compute("backup")
def backup(self, context, instance_id, name, backup_type, rotation,
extra_properties=None):
"""Backup the given instance
@@ -831,6 +833,7 @@ class API(base.Base):
extra_properties=extra_properties)
return recv_meta
+ @scheduler_api.reroute_compute("snapshot")
def snapshot(self, context, instance_id, name, extra_properties=None):
"""Snapshot the given instance.
@@ -873,10 +876,12 @@ class API(base.Base):
params=params)
return recv_meta
+ @scheduler_api.reroute_compute("reboot")
def reboot(self, context, instance_id):
"""Reboot the given instance."""
self._cast_compute_message('reboot_instance', context, instance_id)
+ @scheduler_api.reroute_compute("rebuild")
def rebuild(self, context, instance_id, image_href, name=None,
metadata=None, files_to_inject=None):
"""Rebuild the given instance with the provided metadata."""
@@ -1063,6 +1068,7 @@ class API(base.Base):
"""Unrescue the given instance."""
self._cast_compute_message('unrescue_instance', context, instance_id)
+ @scheduler_api.reroute_compute("set_admin_password")
def set_admin_password(self, context, instance_id, password=None):
"""Set the root/admin password for the given instance."""
host = self._find_host(context, instance_id)
diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py
index c13a629a9..824416514 100644
--- a/nova/compute/instance_types.py
+++ b/nova/compute/instance_types.py
@@ -132,11 +132,8 @@ def get_instance_type_by_name(name):
# flavors.
def get_instance_type_by_flavor_id(flavor_id):
"""Retrieve instance type by flavor_id."""
- if flavor_id is None:
- return get_default_instance_type()
+ ctxt = context.get_admin_context()
try:
- ctxt = context.get_admin_context()
return db.instance_type_get_by_flavor_id(ctxt, flavor_id)
- except exception.DBError, e:
- LOG.exception(_('DB error: %s') % e)
- raise exception.ApiError(_("Unknown flavor: %s") % flavor_id)
+ except ValueError:
+ raise exception.FlavorNotFound(flavor_id=flavor_id)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index a4d2797d6..71febf7c5 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -215,6 +215,15 @@ class ComputeManager(manager.SchedulerDependentManager):
"""This call passes straight through to the virtualization driver."""
return self.driver.refresh_provider_fw_rules()
+ def _get_instance_nw_info(self, context, instance):
+ """Get a list of dictionaries of network data of an instance.
+ Returns an empty list if stub_network flag is set."""
+ network_info = []
+ if not FLAGS.stub_network:
+ network_info = self.network_api.get_instance_nw_info(context,
+ instance)
+ return network_info
+
def _setup_block_device_mapping(self, context, instance_id):
"""setup volumes for block device mapping"""
self.db.instance_set_state(context,
@@ -314,8 +323,6 @@ class ComputeManager(manager.SchedulerDependentManager):
network_info = self.network_api.allocate_for_instance(context,
instance, vpn=is_vpn)
LOG.debug(_("instance network_info: |%s|"), network_info)
- self.network_manager.setup_compute_network(context,
- instance_id)
else:
# TODO(tr3buchet) not really sure how this should be handled.
# virt requires network_info to be passed in but stub_network
@@ -376,6 +383,7 @@ class ComputeManager(manager.SchedulerDependentManager):
{'action_str': action_str, 'instance_id': instance_id},
context=context)
+ network_info = self._get_instance_nw_info(context, instance)
if not FLAGS.stub_network:
self.network_api.deallocate_for_instance(context, instance)
@@ -388,7 +396,7 @@ class ComputeManager(manager.SchedulerDependentManager):
self.db.instance_destroy(context, instance_id)
raise exception.Error(_('trying to destroy already destroyed'
' instance: %s') % instance_id)
- self.driver.destroy(instance)
+ self.driver.destroy(instance, network_info)
if action_str == 'Terminating':
terminate_volumes(self.db, context, instance_id)
@@ -433,11 +441,16 @@ class ComputeManager(manager.SchedulerDependentManager):
self._update_state(context, instance_id, power_state.BUILDING)
- self.driver.destroy(instance_ref)
+ network_info = self._get_instance_nw_info(context, instance_ref)
+
+ self.driver.destroy(instance_ref, network_info)
image_ref = kwargs.get('image_ref')
instance_ref.image_ref = image_ref
instance_ref.injected_files = kwargs.get('injected_files', [])
- self.driver.spawn(instance_ref)
+ network_info = self.network_api.get_instance_nw_info(context,
+ instance_ref)
+ bd_mapping = self._setup_block_device_mapping(context, instance_id)
+ self.driver.spawn(instance_ref, network_info, bd_mapping)
self._update_image_ref(context, instance_id, image_ref)
self._update_launched_at(context, instance_id)
@@ -470,8 +483,8 @@ class ComputeManager(manager.SchedulerDependentManager):
instance_id,
power_state.NOSTATE,
'rebooting')
- self.network_manager.setup_compute_network(context, instance_id)
- self.driver.reboot(instance_ref)
+ network_info = self._get_instance_nw_info(context, instance_ref)
+ self.driver.reboot(instance_ref, network_info)
self._update_state(context, instance_id)
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@@ -661,10 +674,10 @@ class ComputeManager(manager.SchedulerDependentManager):
instance_id,
power_state.NOSTATE,
'rescuing')
- self.network_manager.setup_compute_network(context, instance_id)
_update_state = lambda result: self._update_state_callback(
self, context, instance_id, result)
- self.driver.rescue(instance_ref, _update_state)
+ network_info = self._get_instance_nw_info(context, instance_ref)
+ self.driver.rescue(instance_ref, _update_state, network_info)
self._update_state(context, instance_id)
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@@ -680,7 +693,8 @@ class ComputeManager(manager.SchedulerDependentManager):
'unrescuing')
_update_state = lambda result: self._update_state_callback(
self, context, instance_id, result)
- self.driver.unrescue(instance_ref, _update_state)
+ network_info = self._get_instance_nw_info(context, instance_ref)
+ self.driver.unrescue(instance_ref, _update_state, network_info)
self._update_state(context, instance_id)
@staticmethod
@@ -696,7 +710,8 @@ class ComputeManager(manager.SchedulerDependentManager):
instance_ref = self.db.instance_get_by_uuid(context,
migration_ref.instance_uuid)
- self.driver.destroy(instance_ref)
+ network_info = self._get_instance_nw_info(context, instance_ref)
+ self.driver.destroy(instance_ref, network_info)
usage_info = utils.usage_from_instance(instance_ref)
notifier.notify('compute.%s' % self.host,
'compute.instance.resize.confirm',
@@ -716,7 +731,8 @@ class ComputeManager(manager.SchedulerDependentManager):
instance_ref = self.db.instance_get_by_uuid(context,
migration_ref.instance_uuid)
- self.driver.destroy(instance_ref)
+ network_info = self._get_instance_nw_info(context, instance_ref)
+ self.driver.destroy(instance_ref, network_info)
topic = self.db.queue_get_for(context, FLAGS.compute_topic,
instance_ref['host'])
rpc.cast(context, topic,
@@ -748,7 +764,7 @@ class ComputeManager(manager.SchedulerDependentManager):
local_gb=instance_type['local_gb'],
instance_type_id=instance_type['id']))
- self.driver.revert_resize(instance_ref)
+ self.driver.revert_migration(instance_ref)
self.db.migration_update(context, migration_id,
{'status': 'reverted'})
usage_info = utils.usage_from_instance(instance_ref)
@@ -846,21 +862,26 @@ class ComputeManager(manager.SchedulerDependentManager):
"""
migration_ref = self.db.migration_get(context, migration_id)
+
+ resize_instance = False
instance_ref = self.db.instance_get_by_uuid(context,
migration_ref.instance_uuid)
- instance_type = self.db.instance_type_get_by_flavor_id(context,
- migration_ref['new_flavor_id'])
- self.db.instance_update(context, instance_ref.uuid,
- dict(instance_type_id=instance_type['id'],
- memory_mb=instance_type['memory_mb'],
- vcpus=instance_type['vcpus'],
- local_gb=instance_type['local_gb']))
+ if migration_ref['old_flavor_id'] != migration_ref['new_flavor_id']:
+ instance_type = self.db.instance_type_get_by_flavor_id(context,
+ migration_ref['new_flavor_id'])
+ self.db.instance_update(context, instance_ref.uuid,
+ dict(instance_type_id=instance_type['id'],
+ memory_mb=instance_type['memory_mb'],
+ vcpus=instance_type['vcpus'],
+ local_gb=instance_type['local_gb']))
+ resize_instance = True
instance_ref = self.db.instance_get_by_uuid(context,
instance_ref.uuid)
- network_info = self.network_api.get_instance_nw_info(context,
- instance_ref)
- self.driver.finish_resize(instance_ref, disk_info, network_info)
+
+ network_info = self._get_instance_nw_info(context, instance_ref)
+ self.driver.finish_migration(instance_ref, disk_info, network_info,
+ resize_instance)
self.db.migration_update(context, migration_id,
{'status': 'finished', })
@@ -873,7 +894,7 @@ class ComputeManager(manager.SchedulerDependentManager):
"""
self.network_api.add_fixed_ip_to_instance(context, instance_id,
- network_id)
+ self.host, network_id)
self.inject_network_info(context, instance_id)
self.reset_network(context, instance_id)
@@ -1013,8 +1034,7 @@ class ComputeManager(manager.SchedulerDependentManager):
LOG.debug(_('instance %s: inject network info'), instance_id,
context=context)
instance = self.db.instance_get(context, instance_id)
- network_info = self.network_api.get_instance_nw_info(context,
- instance)
+ network_info = self._get_instance_nw_info(context, instance)
LOG.debug(_("network_info to inject: |%s|"), network_info)
self.driver.inject_network_info(instance, network_info)
@@ -1232,17 +1252,17 @@ class ComputeManager(manager.SchedulerDependentManager):
#
# Retry operation is necessary because continuously request comes,
# concorrent request occurs to iptables, then it complains.
+ network_info = self._get_instance_nw_info(context, instance_ref)
max_retry = FLAGS.live_migration_retry_count
for cnt in range(max_retry):
try:
- self.network_manager.setup_compute_network(context,
- instance_id)
+ self.driver.plug_vifs(instance_ref, network_info)
break
except exception.ProcessExecutionError:
if cnt == max_retry - 1:
raise
else:
- LOG.warn(_("setup_compute_network() failed %(cnt)d."
+ LOG.warn(_("plug_vifs() failed %(cnt)d."
"Retry up to %(max_retry)d for %(hostname)s.")
% locals())
time.sleep(1)
@@ -1320,8 +1340,9 @@ class ComputeManager(manager.SchedulerDependentManager):
# Releasing vlan.
# (not necessary in current implementation?)
+ network_info = self._get_instance_nw_info(ctxt, instance_ref)
# Releasing security group ingress rule.
- self.driver.unfilter_instance(instance_ref)
+ self.driver.unfilter_instance(instance_ref, network_info)
# Database updating.
i_name = instance_ref.name
diff --git a/nova/context.py b/nova/context.py
index 99085ed75..5b2776d4e 100644
--- a/nova/context.py
+++ b/nova/context.py
@@ -18,9 +18,8 @@
"""RequestContext: context for requests that persist through all of nova."""
-import random
+import uuid
-from nova import exception
from nova import utils
@@ -31,86 +30,51 @@ class RequestContext(object):
"""
- def __init__(self, user, project, is_admin=None, read_deleted=False,
- remote_address=None, timestamp=None, request_id=None):
- if hasattr(user, 'id'):
- self._user = user
- self.user_id = user.id
- else:
- self._user = None
- self.user_id = user
- if hasattr(project, 'id'):
- self._project = project
- self.project_id = project.id
- else:
- self._project = None
- self.project_id = project
- if is_admin is None:
- if self.user_id and self.user:
- self.is_admin = self.user.is_admin()
- else:
- self.is_admin = False
- else:
- self.is_admin = is_admin
+ def __init__(self, user_id, project_id, is_admin=None, read_deleted=False,
+ roles=None, remote_address=None, timestamp=None,
+ request_id=None):
+ self.user_id = user_id
+ self.project_id = project_id
+ self.roles = roles or []
+ self.is_admin = is_admin
+ if self.is_admin is None:
+ self.admin = 'admin' in self.roles
self.read_deleted = read_deleted
self.remote_address = remote_address
if not timestamp:
timestamp = utils.utcnow()
- if isinstance(timestamp, str) or isinstance(timestamp, unicode):
- timestamp = utils.parse_isotime(timestamp)
+ if isinstance(timestamp, basestring):
+ timestamp = utils.parse_strtime(timestamp)
self.timestamp = timestamp
if not request_id:
- chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-'
- request_id = ''.join([random.choice(chars) for x in xrange(20)])
+ request_id = unicode(uuid.uuid4())
self.request_id = request_id
- @property
- def user(self):
- # NOTE(vish): Delay import of manager, so that we can import this
- # file from manager.
- from nova.auth import manager
- if not self._user:
- try:
- self._user = manager.AuthManager().get_user(self.user_id)
- except exception.NotFound:
- pass
- return self._user
-
- @property
- def project(self):
- # NOTE(vish): Delay import of manager, so that we can import this
- # file from manager.
- from nova.auth import manager
- if not self._project:
- try:
- auth_manager = manager.AuthManager()
- self._project = auth_manager.get_project(self.project_id)
- except exception.NotFound:
- pass
- return self._project
-
def to_dict(self):
- return {'user': self.user_id,
- 'project': self.project_id,
+ return {'user_id': self.user_id,
+ 'project_id': self.project_id,
'is_admin': self.is_admin,
'read_deleted': self.read_deleted,
+ 'roles': self.roles,
'remote_address': self.remote_address,
- 'timestamp': utils.isotime(self.timestamp),
+ 'timestamp': utils.strtime(self.timestamp),
'request_id': self.request_id}
@classmethod
def from_dict(cls, values):
return cls(**values)
- def elevated(self, read_deleted=False):
+ def elevated(self, read_deleted=None):
"""Return a version of this context with admin flag set."""
- return RequestContext(self.user_id,
- self.project_id,
- True,
- read_deleted,
- self.remote_address,
- self.timestamp,
- self.request_id)
+ rd = self.read_deleted if read_deleted is None else read_deleted
+ return RequestContext(user_id=self.user_id,
+ project_id=self.project_id,
+ is_admin=True,
+ read_deleted=rd,
+ roles=self.roles,
+ remote_address=self.remote_address,
+ timestamp=self.timestamp,
+ request_id=self.request_id)
def get_admin_context(read_deleted=False):
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index abfa6a3b7..f4af0bdbe 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -63,7 +63,7 @@ def is_user_context(context):
def authorize_project_context(context, project_id):
"""Ensures a request has permission to access the given project."""
if is_user_context(context):
- if not context.project:
+ if not context.project_id:
raise exception.NotAuthorized()
elif context.project_id != project_id:
raise exception.NotAuthorized()
@@ -72,7 +72,7 @@ def authorize_project_context(context, project_id):
def authorize_user_context(context, user_id):
"""Ensures a request has permission to access the given user."""
if is_user_context(context):
- if not context.user:
+ if not context.user_id:
raise exception.NotAuthorized()
elif context.user_id != user_id:
raise exception.NotAuthorized()
@@ -1174,9 +1174,9 @@ def instance_get_active_by_window(context, begin, end=None):
"""Return instances that were continuously active over the given window"""
session = get_session()
query = session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
+ options(joinedload_all('fixed_ips.floating_ips')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('instance_type')).\
filter(models.Instance.launched_at < begin)
if end:
@@ -1270,7 +1270,7 @@ def instance_get_project_vpn(context, project_id):
options(joinedload_all('fixed_ips.floating_ips')).\
options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
+ options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(project_id=project_id).\
@@ -1313,7 +1313,7 @@ def instance_get_fixed_addresses_v6(context, instance_id):
# combine prefixes, macs, and project_id into (prefix,mac,p_id) tuples
prefix_mac_tuples = zip(prefixes, macs, [project_id for m in macs])
# return list containing ipv6 address for each tuple
- return [ipv6.to_global_ipv6(*t) for t in prefix_mac_tuples]
+ return [ipv6.to_global(*t) for t in prefix_mac_tuples]
@require_context
@@ -3264,8 +3264,8 @@ def agent_build_destroy(context, agent_build_id):
with session.begin():
session.query(models.AgentBuild).\
filter_by(id=agent_build_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
+ update({'deleted': True,
+ 'deleted_at': utils.utcnow(),
'updated_at': literal_column('updated_at')})
@@ -3314,7 +3314,7 @@ def instance_type_extra_specs_delete(context, instance_type_id, key):
def instance_type_extra_specs_get_item(context, instance_type_id, key):
session = get_session()
- sppec_result = session.query(models.InstanceTypeExtraSpecs).\
+ spec_result = session.query(models.InstanceTypeExtraSpecs).\
filter_by(instance_type_id=instance_type_id).\
filter_by(key=key).\
filter_by(deleted=False).\
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py
index b002ba064..4e8eaf0fd 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py
@@ -17,6 +17,7 @@
from sqlalchemy import Column, Integer, String, MetaData, Table
+
meta = MetaData()
@@ -33,6 +34,14 @@ def upgrade(migrate_engine):
meta.bind = migrate_engine
migrations = Table('migrations', meta, autoload=True)
migrations.create_column(instance_uuid)
+
+ if migrate_engine.name == "mysql":
+ try:
+ migrate_engine.execute("ALTER TABLE migrations DROP FOREIGN KEY " \
+ "`migrations_ibfk_1`;")
+ except Exception: # Don't care, just fail silently.
+ pass
+
migrations.c.instance_id.drop()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 7e35c2cba..056259539 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -177,14 +177,6 @@ class Instance(BASE, NovaBase):
user_id = Column(String(255))
project_id = Column(String(255))
- @property
- def user(self):
- return auth.manager.AuthManager().get_user(self.user_id)
-
- @property
- def project(self):
- return auth.manager.AuthManager().get_project(self.project_id)
-
image_ref = Column(String(255))
kernel_id = Column(String(255))
ramdisk_id = Column(String(255))
@@ -465,14 +457,6 @@ class SecurityGroup(BASE, NovaBase):
'Instance.deleted == False)',
backref='security_groups')
- @property
- def user(self):
- return auth.manager.AuthManager().get_user(self.user_id)
-
- @property
- def project(self):
- return auth.manager.AuthManager().get_project(self.project_id)
-
class SecurityGroupIngressRule(BASE, NovaBase):
"""Represents a rule in a security group."""
diff --git a/nova/exception.py b/nova/exception.py
index 38e705417..8c9b45a80 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -116,7 +116,8 @@ def wrap_exception(notifier=None, publisher_id=None, event_type=None,
notifier.notify(publisher_id, temp_type, temp_level,
payload)
- if not isinstance(e, Error):
+ if (not isinstance(e, Error) and
+ not isinstance(e, NovaException)):
#exc_type, exc_value, exc_traceback = sys.exc_info()
LOG.exception(_('Uncaught exception'))
#logging.error(traceback.extract_stack(exc_traceback))
@@ -372,6 +373,10 @@ class StorageRepositoryNotFound(NotFound):
message = _("Cannot find SR to read/write VDI.")
+class NetworkNotCreated(NovaException):
+ message = _("%(req)s is required to create a network.")
+
+
class NetworkNotFound(NotFound):
message = _("Network %(network_id)s could not be found.")
diff --git a/nova/flags.py b/nova/flags.py
index 49355b436..fa6d8860a 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -343,7 +343,7 @@ DEFINE_string('lock_path', os.path.join(os.path.dirname(__file__), '../'),
'Directory for lock files')
DEFINE_string('logdir', None, 'output to a per-service log file in named '
'directory')
-
+DEFINE_integer('logfile_mode', 0644, 'Default file mode of the logs.')
DEFINE_string('sqlite_db', 'nova.sqlite', 'file name for sqlite')
DEFINE_string('sql_connection',
'sqlite:///$state_path/$sqlite_db',
diff --git a/nova/image/s3.py b/nova/image/s3.py
index c313c7a13..ccbfa89cd 100644
--- a/nova/image/s3.py
+++ b/nova/image/s3.py
@@ -34,7 +34,6 @@ from nova import flags
from nova import image
from nova import log as logging
from nova import utils
-from nova.auth import manager
from nova.image import service
from nova.api.ec2 import ec2utils
@@ -43,6 +42,10 @@ LOG = logging.getLogger("nova.image.s3")
FLAGS = flags.FLAGS
flags.DEFINE_string('image_decryption_dir', '/tmp',
'parent dir for tempdir used for image decryption')
+flags.DEFINE_string('s3_access_key', 'notchecked',
+ 'access key to use for s3 server for images')
+flags.DEFINE_string('s3_secret_key', 'notchecked',
+ 'secret key to use for s3 server for images')
class S3ImageService(service.BaseImageService):
@@ -82,11 +85,10 @@ class S3ImageService(service.BaseImageService):
@staticmethod
def _conn(context):
- # TODO(vish): is there a better way to get creds to sign
- # for the user?
- access = manager.AuthManager().get_access_key(context.user,
- context.project)
- secret = str(context.user.secret)
+ # NOTE(vish): access and secret keys for s3 server are not
+ # checked in nova-objectstore
+ access = FLAGS.s3_access_key
+ secret = FLAGS.s3_secret_key
calling = boto.s3.connection.OrdinaryCallingFormat()
return boto.s3.connection.S3Connection(aws_access_key_id=access,
aws_secret_access_key=secret,
diff --git a/nova/log.py b/nova/log.py
index f8c0ba68d..222b8c5fb 100644
--- a/nova/log.py
+++ b/nova/log.py
@@ -43,8 +43,8 @@ from nova import version
FLAGS = flags.FLAGS
flags.DEFINE_string('logging_context_format_string',
'%(asctime)s %(levelname)s %(name)s '
- '[%(request_id)s %(user)s '
- '%(project)s] %(message)s',
+ '[%(request_id)s %(user_id)s '
+ '%(project_id)s] %(message)s',
'format string to use for log messages with context')
flags.DEFINE_string('logging_default_format_string',
'%(asctime)s %(levelname)s %(name)s [-] '
@@ -257,6 +257,7 @@ class NovaRootLogger(NovaLogger):
self.filelog = WatchedFileHandler(logpath)
self.addHandler(self.filelog)
self.logpath = logpath
+ os.chmod(self.logpath, FLAGS.logfile_mode)
else:
self.removeHandler(self.filelog)
self.addHandler(self.streamlog)
diff --git a/nova/network/api.py b/nova/network/api.py
index c2360f0d0..247768722 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -45,6 +45,10 @@ class API(base.Base):
context.project_id)
return ips
+ def get_vifs_by_instance(self, context, instance_id):
+ vifs = self.db.virtual_interface_get_by_instance(context, instance_id)
+ return vifs
+
def allocate_floating_ip(self, context):
"""Adds a floating ip to a project."""
# NOTE(vish): We don't know which network host should get the ip
@@ -160,9 +164,10 @@ class API(base.Base):
{'method': 'deallocate_for_instance',
'args': args})
- def add_fixed_ip_to_instance(self, context, instance_id, network_id):
+ def add_fixed_ip_to_instance(self, context, instance_id, host, network_id):
"""Adds a fixed ip to instance from specified network."""
args = {'instance_id': instance_id,
+ 'host': host,
'network_id': network_id}
rpc.cast(context, FLAGS.network_topic,
{'method': 'add_fixed_ip_to_instance',
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index a8ce1c16a..8ace07884 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -455,6 +455,7 @@ def ensure_vlan_bridge(vlan_num, bridge, bridge_interface, net_attrs=None):
"""Create a vlan and bridge unless they already exist."""
interface = ensure_vlan(vlan_num, bridge_interface)
ensure_bridge(bridge, interface, net_attrs)
+ return interface
@utils.synchronized('ensure_vlan', external=True)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 4f984296c..4a3791d8a 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -68,7 +68,7 @@ LOG = logging.getLogger("nova.network.manager")
FLAGS = flags.FLAGS
-flags.DEFINE_string('flat_network_bridge', 'br100',
+flags.DEFINE_string('flat_network_bridge', None,
'Bridge for simple network instances')
flags.DEFINE_string('flat_network_dns', '8.8.4.4',
'Dns for simple network')
@@ -258,7 +258,7 @@ class FloatingIP(object):
# NOTE(tr3buchet): all networks hosts in zone now use the same pool
LOG.debug("QUOTA: %s" % quota.allowed_floating_ips(context, 1))
if quota.allowed_floating_ips(context, 1) < 1:
- LOG.warn(_('Quota exceeeded for %s, tried to allocate '
+ LOG.warn(_('Quota exceeded for %s, tried to allocate '
'address'),
context.project_id)
raise quota.QuotaError(_('Address quota exceeded. You cannot '
@@ -300,6 +300,12 @@ class NetworkManager(manager.SchedulerDependentManager):
The one at a time part is to flatten the layout to help scale
"""
+ # If True, this manager requires VIF to create a bridge.
+ SHOULD_CREATE_BRIDGE = False
+
+ # If True, this manager requires VIF to create VLAN tag.
+ SHOULD_CREATE_VLAN = False
+
timeout_fixed_ips = True
def __init__(self, network_driver=None, *args, **kwargs):
@@ -426,7 +432,12 @@ class NetworkManager(manager.SchedulerDependentManager):
and info = dict containing pertinent networking data
"""
# TODO(tr3buchet) should handle floating IPs as well?
- fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id)
+ try:
+ fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id)
+ except exception.FixedIpNotFoundForInstance:
+ LOG.warn(_('No fixed IPs for instance %s'), instance_id)
+ fixed_ips = []
+
vifs = self.db.virtual_interface_get_by_instance(context, instance_id)
flavor = self.db.instance_type_get(context, instance_type_id)
network_info = []
@@ -458,7 +469,10 @@ class NetworkManager(manager.SchedulerDependentManager):
'id': network['id'],
'cidr': network['cidr'],
'cidr_v6': network['cidr_v6'],
- 'injected': network['injected']}
+ 'injected': network['injected'],
+ 'vlan': network['vlan'],
+ 'bridge_interface': network['bridge_interface'],
+ 'multi_host': network['multi_host']}
if network['multi_host']:
dhcp_server = self._get_dhcp_ip(context, network, host)
else:
@@ -473,7 +487,10 @@ class NetworkManager(manager.SchedulerDependentManager):
'mac': vif['address'],
'rxtx_cap': flavor['rxtx_cap'],
'dns': [],
- 'ips': [ip_dict(ip) for ip in network_IPs]}
+ 'ips': [ip_dict(ip) for ip in network_IPs],
+ 'should_create_bridge': self.SHOULD_CREATE_BRIDGE,
+ 'should_create_vlan': self.SHOULD_CREATE_VLAN}
+
if network['cidr_v6']:
info['ip6s'] = [ip6_dict()]
# TODO(tr3buchet): handle ip6 routes here as well
@@ -597,12 +614,13 @@ class NetworkManager(manager.SchedulerDependentManager):
bridge_interface, dns1=None, dns2=None, **kwargs):
"""Create networks based on parameters."""
fixed_net = netaddr.IPNetwork(cidr)
- fixed_net_v6 = netaddr.IPNetwork(cidr_v6)
- significant_bits_v6 = 64
- network_size_v6 = 1 << 64
+ if FLAGS.use_ipv6:
+ fixed_net_v6 = netaddr.IPNetwork(cidr_v6)
+ significant_bits_v6 = 64
+ network_size_v6 = 1 << 64
+
for index in range(num_networks):
start = index * network_size
- start_v6 = index * network_size_v6
significant_bits = 32 - int(math.log(network_size, 2))
cidr = '%s/%s' % (fixed_net[start], significant_bits)
project_net = netaddr.IPNetwork(cidr)
@@ -623,6 +641,7 @@ class NetworkManager(manager.SchedulerDependentManager):
net['label'] = label
if FLAGS.use_ipv6:
+ start_v6 = index * network_size_v6
cidr_v6 = '%s/%s' % (fixed_net_v6[start_v6],
significant_bits_v6)
net['cidr_v6'] = cidr_v6
@@ -698,22 +717,14 @@ class NetworkManager(manager.SchedulerDependentManager):
"""Sets up network on this host."""
raise NotImplementedError()
- def setup_compute_network(self, context, instance_id):
- """Sets up matching network for compute hosts.
-
- this code is run on and by the compute host, not on network
- hosts
- """
- raise NotImplementedError()
-
class FlatManager(NetworkManager):
"""Basic network where no vlans are used.
FlatManager does not do any bridge or vlan creation. The user is
- responsible for setting up whatever bridge is specified in
- flat_network_bridge (br100 by default). This bridge needs to be created
- on all compute hosts.
+ responsible for setting up whatever bridges are specified when creating
+ networks through nova-manage. This bridge needs to be created on all
+ compute hosts.
The idea is to create a single network for the host with a command like:
nova-manage network create 192.168.0.0/24 1 256. Creating multiple
@@ -749,13 +760,6 @@ class FlatManager(NetworkManager):
**kwargs)
self.db.fixed_ip_disassociate(context, address)
- def setup_compute_network(self, context, instance_id):
- """Network is created manually.
-
- this code is run on and by the compute host, not on network hosts
- """
- pass
-
def _setup_network(self, context, network_ref):
"""Setup Network on this host."""
net = {}
@@ -772,6 +776,8 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
"""
+ SHOULD_CREATE_BRIDGE = True
+
def init_host(self):
"""Do any initialization that needs to be run if this is a
standalone service.
@@ -784,17 +790,6 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
self.driver.metadata_forward()
- def setup_compute_network(self, context, instance_id):
- """Sets up matching networks for compute hosts.
-
- this code is run on and by the compute host, not on network hosts
- """
- networks = db.network_get_all_by_instance(context, instance_id)
- for network in networks:
- if not network['multi_host']:
- self.driver.ensure_bridge(network['bridge'],
- network['bridge_interface'])
-
def _setup_network(self, context, network_ref):
"""Sets up network on this host."""
network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref)
@@ -825,6 +820,9 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
"""
+ SHOULD_CREATE_BRIDGE = True
+ SHOULD_CREATE_VLAN = True
+
def init_host(self):
"""Do any initialization that needs to be run if this is a
standalone service.
@@ -863,17 +861,6 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
"""Force adds another network to a project."""
self.db.network_associate(context, project_id, force=True)
- def setup_compute_network(self, context, instance_id):
- """Sets up matching network for compute hosts.
- this code is run on and by the compute host, not on network hosts
- """
- networks = self.db.network_get_all_by_instance(context, instance_id)
- for network in networks:
- if not network['multi_host']:
- self.driver.ensure_vlan_bridge(network['vlan'],
- network['bridge'],
- network['bridge_interface'])
-
def _get_networks_for_instance(self, context, instance_id, project_id):
"""Determine which networks an instance should connect to."""
# get networks associated with project
diff --git a/nova/network/vmwareapi_net.py b/nova/network/vmwareapi_net.py
deleted file mode 100644
index b32cf3303..000000000
--- a/nova/network/vmwareapi_net.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright (c) 2011 Citrix Systems, Inc.
-# 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.
-
-"""Implements vlans for vmwareapi."""
-
-from nova import db
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import utils
-from nova.virt.vmwareapi_conn import VMWareAPISession
-from nova.virt.vmwareapi import network_utils
-
-
-LOG = logging.getLogger("nova.network.vmwareapi_net")
-
-
-FLAGS = flags.FLAGS
-FLAGS['vlan_interface'].SetDefault('vmnic0')
-
-
-def ensure_vlan_bridge(vlan_num, bridge, bridge_interface, net_attrs=None):
- """Create a vlan and bridge unless they already exist."""
- # Open vmwareapi session
- host_ip = FLAGS.vmwareapi_host_ip
- host_username = FLAGS.vmwareapi_host_username
- host_password = FLAGS.vmwareapi_host_password
- if not host_ip or host_username is None or host_password is None:
- raise Exception(_('Must specify vmwareapi_host_ip, '
- 'vmwareapi_host_username '
- 'and vmwareapi_host_password to use '
- 'connection_type=vmwareapi'))
- session = VMWareAPISession(host_ip, host_username, host_password,
- FLAGS.vmwareapi_api_retry_count)
- vlan_interface = bridge_interface
- # Check if the vlan_interface physical network adapter exists on the host
- if not network_utils.check_if_vlan_interface_exists(session,
- vlan_interface):
- raise exception.NetworkAdapterNotFound(adapter=vlan_interface)
-
- # Get the vSwitch associated with the Physical Adapter
- vswitch_associated = network_utils.get_vswitch_for_vlan_interface(
- session, vlan_interface)
- if vswitch_associated is None:
- raise exception.SwicthNotFoundForNetworkAdapter(adapter=vlan_interface)
- # Check whether bridge already exists and retrieve the the ref of the
- # network whose name_label is "bridge"
- network_ref = network_utils.get_network_with_the_name(session, bridge)
- if network_ref is None:
- # Create a port group on the vSwitch associated with the vlan_interface
- # corresponding physical network adapter on the ESX host
- network_utils.create_port_group(session, bridge, vswitch_associated,
- vlan_num)
- else:
- # Get the vlan id and vswitch corresponding to the port group
- pg_vlanid, pg_vswitch = \
- network_utils.get_vlanid_and_vswitch_for_portgroup(session, bridge)
-
- # Check if the vswitch associated is proper
- if pg_vswitch != vswitch_associated:
- raise exception.InvalidVLANPortGroup(bridge=bridge,
- expected=vswitch_associated,
- actual=pg_vswitch)
-
- # Check if the vlan id is proper for the port group
- if pg_vlanid != vlan_num:
- raise exception.InvalidVLANTag(bridge=bridge, tag=vlan_num,
- pgroup=pg_vlanid)
diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py
deleted file mode 100644
index e86f4017d..000000000
--- a/nova/network/xenapi_net.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright (c) 2011 Citrix Systems, Inc.
-# 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.
-
-"""Implements vlans, bridges, and iptables rules using linux utilities."""
-
-import os
-
-from nova import db
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import utils
-from nova.virt import xenapi_conn
-from nova.virt.xenapi import network_utils
-
-
-LOG = logging.getLogger("nova.xenapi_net")
-
-
-FLAGS = flags.FLAGS
-
-
-def ensure_vlan_bridge(vlan_num, bridge, bridge_interface, net_attrs=None):
- """Create a vlan and bridge unless they already exist."""
- # Open xenapi session
- LOG.debug('ENTERING ensure_vlan_bridge in xenapi net')
- url = FLAGS.xenapi_connection_url
- username = FLAGS.xenapi_connection_username
- password = FLAGS.xenapi_connection_password
- session = xenapi_conn.XenAPISession(url, username, password)
- # Check whether bridge already exists
- # Retrieve network whose name_label is "bridge"
- network_ref = network_utils.NetworkHelper.find_network_with_name_label(
- session,
- bridge)
- if network_ref is None:
- # If bridge does not exists
- # 1 - create network
- description = 'network for nova bridge %s' % bridge
- network_rec = {'name_label': bridge,
- 'name_description': description,
- 'other_config': {}}
- network_ref = session.call_xenapi('network.create', network_rec)
- # 2 - find PIF for VLAN
- # NOTE(salvatore-orlando): using double quotes inside single quotes
- # as xapi filter only support tokens in double quotes
- expr = 'field "device" = "%s" and \
- field "VLAN" = "-1"' % bridge_interface
- pifs = session.call_xenapi('PIF.get_all_records_where', expr)
- pif_ref = None
- # Multiple PIF are ok: we are dealing with a pool
- if len(pifs) == 0:
- raise Exception(
- _('Found no PIF for device %s') % bridge_interface)
- # 3 - create vlan for network
- for pif_ref in pifs.keys():
- session.call_xenapi('VLAN.create',
- pif_ref,
- str(vlan_num),
- network_ref)
- else:
- # Check VLAN tag is appropriate
- network_rec = session.call_xenapi('network.get_record', network_ref)
- # Retrieve PIFs from network
- for pif_ref in network_rec['PIFs']:
- # Retrieve VLAN from PIF
- pif_rec = session.call_xenapi('PIF.get_record', pif_ref)
- pif_vlan = int(pif_rec['VLAN'])
- # Raise an exception if VLAN != vlan_num
- if pif_vlan != vlan_num:
- raise Exception(_("PIF %(pif_rec['uuid'])s for network "
- "%(bridge)s has VLAN id %(pif_vlan)d. "
- "Expected %(vlan_num)d") % locals())
diff --git a/nova/rpc/__init__.py b/nova/rpc/__init__.py
new file mode 100644
index 000000000..bdf7f705b
--- /dev/null
+++ b/nova/rpc/__init__.py
@@ -0,0 +1,66 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from nova.utils import import_object
+from nova.rpc.common import RemoteError, LOG
+from nova import flags
+
+FLAGS = flags.FLAGS
+flags.DEFINE_string('rpc_backend',
+ 'nova.rpc.amqp',
+ "The messaging module to use, defaults to AMQP.")
+
+RPCIMPL = import_object(FLAGS.rpc_backend)
+
+
+def create_connection(new=True):
+ return RPCIMPL.Connection.instance(new=True)
+
+
+def create_consumer(conn, topic, proxy, fanout=False):
+ if fanout:
+ return RPCIMPL.FanoutAdapterConsumer(
+ connection=conn,
+ topic=topic,
+ proxy=proxy)
+ else:
+ return RPCIMPL.TopicAdapterConsumer(
+ connection=conn,
+ topic=topic,
+ proxy=proxy)
+
+
+def create_consumer_set(conn, consumers):
+ return RPCIMPL.ConsumerSet(connection=conn, consumer_list=consumers)
+
+
+def call(context, topic, msg):
+ return RPCIMPL.call(context, topic, msg)
+
+
+def cast(context, topic, msg):
+ return RPCIMPL.cast(context, topic, msg)
+
+
+def fanout_cast(context, topic, msg):
+ return RPCIMPL.fanout_cast(context, topic, msg)
+
+
+def multicall(context, topic, msg):
+ return RPCIMPL.multicall(context, topic, msg)
diff --git a/nova/rpc.py b/nova/rpc/amqp.py
index e2771ca88..61555795a 100644
--- a/nova/rpc.py
+++ b/nova/rpc/amqp.py
@@ -44,9 +44,7 @@ from nova import fakerabbit
from nova import flags
from nova import log as logging
from nova import utils
-
-
-LOG = logging.getLogger('nova.rpc')
+from nova.rpc.common import RemoteError, LOG
FLAGS = flags.FLAGS
@@ -418,25 +416,6 @@ def msg_reply(msg_id, reply=None, failure=None):
publisher.close()
-class RemoteError(exception.Error):
- """Signifies that a remote class has raised an exception.
-
- Containes a string representation of the type of the original exception,
- the value of the original exception, and the traceback. These are
- sent to the parent as a joined string so printing the exception
- contains all of the relevent info.
-
- """
-
- def __init__(self, exc_type, value, traceback):
- self.exc_type = exc_type
- self.value = value
- self.traceback = traceback
- super(RemoteError, self).__init__('%s %s\n%s' % (exc_type,
- value,
- traceback))
-
-
def _unpack_context(msg):
"""Unpack context from msg."""
context_dict = {}
diff --git a/nova/rpc/common.py b/nova/rpc/common.py
new file mode 100644
index 000000000..1d3065a83
--- /dev/null
+++ b/nova/rpc/common.py
@@ -0,0 +1,23 @@
+from nova import exception
+from nova import log as logging
+
+LOG = logging.getLogger('nova.rpc')
+
+
+class RemoteError(exception.Error):
+ """Signifies that a remote class has raised an exception.
+
+ Containes a string representation of the type of the original exception,
+ the value of the original exception, and the traceback. These are
+ sent to the parent as a joined string so printing the exception
+ contains all of the relevent info.
+
+ """
+
+ def __init__(self, exc_type, value, traceback):
+ self.exc_type = exc_type
+ self.value = value
+ self.traceback = traceback
+ super(RemoteError, self).__init__('%s %s\n%s' % (exc_type,
+ value,
+ traceback))
diff --git a/nova/scheduler/least_cost.py b/nova/scheduler/least_cost.py
index 6f5eb66fd..8c400d476 100644
--- a/nova/scheduler/least_cost.py
+++ b/nova/scheduler/least_cost.py
@@ -28,6 +28,7 @@ from nova import flags
from nova import log as logging
from nova.scheduler import zone_aware_scheduler
from nova import utils
+from nova import exception
LOG = logging.getLogger('nova.scheduler.least_cost')
diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py
index c429fdfcc..d99d7214c 100644
--- a/nova/scheduler/zone_aware_scheduler.py
+++ b/nova/scheduler/zone_aware_scheduler.py
@@ -81,7 +81,7 @@ class ZoneAwareScheduler(driver.Scheduler):
decryptor = crypto.decryptor(FLAGS.build_plan_encryption_key)
try:
json_entry = decryptor(blob)
- return json.dumps(entry)
+ return json.dumps(json_entry)
except M2Crypto.EVP.EVPError:
pass
return None
diff --git a/nova/service.py b/nova/service.py
index 00e4f61e5..6e9eddc5a 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -149,26 +149,22 @@ class Service(object):
if 'nova-compute' == self.binary:
self.manager.update_available_resource(ctxt)
- self.conn = rpc.Connection.instance(new=True)
+ self.conn = rpc.create_connection(new=True)
logging.debug("Creating Consumer connection for Service %s" %
self.topic)
# Share this same connection for these Consumers
- consumer_all = rpc.TopicAdapterConsumer(
- connection=self.conn,
- topic=self.topic,
- proxy=self)
- consumer_node = rpc.TopicAdapterConsumer(
- connection=self.conn,
- topic='%s.%s' % (self.topic, self.host),
- proxy=self)
- fanout = rpc.FanoutAdapterConsumer(
- connection=self.conn,
- topic=self.topic,
- proxy=self)
- consumer_set = rpc.ConsumerSet(
- connection=self.conn,
- consumer_list=[consumer_all, consumer_node, fanout])
+ consumer_all = rpc.create_consumer(self.conn, self.topic, self,
+ fanout=False)
+
+ node_topic = '%s.%s' % (self.topic, self.host)
+ consumer_node = rpc.create_consumer(self.conn, node_topic, self,
+ fanout=False)
+
+ fanout = rpc.create_consumer(self.conn, self.topic, self, fanout=True)
+
+ consumers = [consumer_all, consumer_node, fanout]
+ consumer_set = rpc.create_consumer_set(self.conn, consumers)
# Wait forever, processing these consumers
def _wait():
diff --git a/nova/test.py b/nova/test.py
index 9790b0aa1..549aa6fcf 100644
--- a/nova/test.py
+++ b/nova/test.py
@@ -99,9 +99,7 @@ class TestCase(unittest.TestCase):
self.flag_overrides = {}
self.injected = []
self._services = []
- self._monkey_patch_attach()
self._original_flags = FLAGS.FlagValuesDict()
- rpc.ConnectionPool = rpc.Pool(max_size=FLAGS.rpc_conn_pool_size)
def tearDown(self):
"""Runs after each test method to tear down test environment."""
@@ -126,9 +124,6 @@ class TestCase(unittest.TestCase):
# Reset any overriden flags
self.reset_flags()
- # Reset our monkey-patches
- rpc.Consumer.attach_to_eventlet = self.original_attach
-
# Stop any timers
for x in self.injected:
try:
@@ -172,17 +167,6 @@ class TestCase(unittest.TestCase):
self._services.append(svc)
return svc
- def _monkey_patch_attach(self):
- self.original_attach = rpc.Consumer.attach_to_eventlet
-
- def _wrapped(inner_self):
- rv = self.original_attach(inner_self)
- self.injected.append(rv)
- return rv
-
- _wrapped.func_name = self.original_attach.func_name
- rpc.Consumer.attach_to_eventlet = _wrapped
-
# Useful assertions
def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
"""Assert two dicts are equivalent.
diff --git a/nova/tests/api/openstack/contrib/test_floating_ips.py b/nova/tests/api/openstack/contrib/test_floating_ips.py
index de006d088..50ad7de08 100644
--- a/nova/tests/api/openstack/contrib/test_floating_ips.py
+++ b/nova/tests/api/openstack/contrib/test_floating_ips.py
@@ -74,12 +74,8 @@ class FloatingIpTest(test.TestCase):
def setUp(self):
super(FloatingIpTest, self).setUp()
self.controller = FloatingIPController()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
self.stubs.Set(network.api.API, "get_floating_ip",
network_api_get_floating_ip)
self.stubs.Set(network.api.API, "list_floating_ips",
@@ -96,7 +92,6 @@ class FloatingIpTest(test.TestCase):
self._create_floating_ip()
def tearDown(self):
- self.stubs.UnsetAll()
self._delete_floating_ip()
super(FloatingIpTest, self).tearDown()
diff --git a/nova/tests/api/openstack/contrib/test_multinic_xs.py b/nova/tests/api/openstack/contrib/test_multinic_xs.py
index b0a9f7676..ac28f6be6 100644
--- a/nova/tests/api/openstack/contrib/test_multinic_xs.py
+++ b/nova/tests/api/openstack/contrib/test_multinic_xs.py
@@ -42,22 +42,14 @@ def compute_api_remove_fixed_ip(self, context, instance_id, address):
class FixedIpTest(test.TestCase):
def setUp(self):
super(FixedIpTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
self.stubs.Set(compute.api.API, "add_fixed_ip",
compute_api_add_fixed_ip)
self.stubs.Set(compute.api.API, "remove_fixed_ip",
compute_api_remove_fixed_ip)
self.context = context.get_admin_context()
- def tearDown(self):
- self.stubs.UnsetAll()
- super(FixedIpTest, self).tearDown()
-
def test_add_fixed_ip(self):
global last_add_fixed_ip
last_add_fixed_ip = (None, None)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 26b1de818..28969d5f8 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -29,6 +29,7 @@ from glance.common import exception as glance_exc
from nova import context
from nova import exception as exc
from nova import utils
+from nova import wsgi
import nova.api.openstack.auth
from nova.api import openstack
from nova.api.openstack import auth
@@ -40,14 +41,13 @@ import nova.image.fake
from nova.image import glance
from nova.image import service
from nova.tests import fake_flags
-from nova.wsgi import Router
class Context(object):
pass
-class FakeRouter(Router):
+class FakeRouter(wsgi.Router):
def __init__(self):
pass
@@ -68,21 +68,30 @@ def fake_auth_init(self, application):
@webob.dec.wsgify
def fake_wsgi(self, req):
- req.environ['nova.context'] = context.RequestContext(1, 1)
return self.application
-def wsgi_app(inner_app10=None, inner_app11=None):
+def wsgi_app(inner_app10=None, inner_app11=None, fake_auth=True):
if not inner_app10:
inner_app10 = openstack.APIRouterV10()
if not inner_app11:
inner_app11 = openstack.APIRouterV11()
- mapper = urlmap.URLMap()
- api10 = openstack.FaultWrapper(auth.AuthMiddleware(
+
+ if fake_auth:
+ ctxt = context.RequestContext('fake', 'fake')
+ api10 = openstack.FaultWrapper(wsgi.InjectContext(ctxt,
+ limits.RateLimitingMiddleware(inner_app10)))
+ api11 = openstack.FaultWrapper(wsgi.InjectContext(ctxt,
+ limits.RateLimitingMiddleware(
+ extensions.ExtensionMiddleware(inner_app11))))
+ else:
+ api10 = openstack.FaultWrapper(auth.AuthMiddleware(
limits.RateLimitingMiddleware(inner_app10)))
- api11 = openstack.FaultWrapper(auth.AuthMiddleware(
+ api11 = openstack.FaultWrapper(auth.AuthMiddleware(
limits.RateLimitingMiddleware(
extensions.ExtensionMiddleware(inner_app11))))
+ Auth = auth
+ mapper = urlmap.URLMap()
mapper['/v1.0'] = api10
mapper['/v1.1'] = api11
mapper['/'] = openstack.FaultWrapper(versions.Versions())
@@ -359,17 +368,18 @@ class FakeAuthManager(object):
if admin is not None:
user.admin = admin
- def is_admin(self, user):
+ def is_admin(self, user_id):
+ user = self.get_user(user_id)
return user.admin
- def is_project_member(self, user, project):
+ def is_project_member(self, user_id, project):
if not isinstance(project, Project):
try:
project = self.get_project(project)
except exc.NotFound:
raise webob.exc.HTTPUnauthorized()
- return ((user.id in project.member_ids) or
- (user.id == project.project_manager_id))
+ return ((user_id in project.member_ids) or
+ (user_id == project.project_manager_id))
def create_project(self, name, manager_user, description=None,
member_users=None):
@@ -396,13 +406,13 @@ class FakeAuthManager(object):
else:
raise exc.NotFound
- def get_projects(self, user=None):
- if not user:
+ def get_projects(self, user_id=None):
+ if not user_id:
return FakeAuthManager.projects.values()
else:
return [p for p in FakeAuthManager.projects.values()
- if (user.id in p.member_ids) or
- (user.id == p.project_manager_id)]
+ if (user_id in p.member_ids) or
+ (user_id == p.project_manager_id)]
class FakeRateLimiter(object):
diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py
index 64abcf48c..89dbf5213 100644
--- a/nova/tests/api/openstack/test_accounts.py
+++ b/nova/tests/api/openstack/test_accounts.py
@@ -16,7 +16,6 @@
import json
-import stubout
import webob
from nova import flags
@@ -41,7 +40,7 @@ def fake_admin_check(self, req):
class AccountsTest(test.TestCase):
def setUp(self):
super(AccountsTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
+ self.flags(allow_admin_api=True)
self.stubs.Set(accounts.Controller, '__init__',
fake_init)
self.stubs.Set(accounts.Controller, '_check_admin',
@@ -52,8 +51,6 @@ class AccountsTest(test.TestCase):
fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_auth(self.stubs)
- self.allow_admin = FLAGS.allow_admin_api
- FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
joeuser = User('id1', 'guy1', 'acc1', 'secret1', False)
superuser = User('id2', 'guy2', 'acc2', 'secret2', True)
@@ -62,11 +59,6 @@ class AccountsTest(test.TestCase):
fakemgr.create_project('test1', joeuser)
fakemgr.create_project('test2', superuser)
- def tearDown(self):
- self.stubs.UnsetAll()
- FLAGS.allow_admin_api = self.allow_admin
- super(AccountsTest, self).tearDown()
-
def test_get_account(self):
req = webob.Request.blank('/v1.0/accounts/test1')
res = req.get_response(fakes.wsgi_app())
diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py
index e87255b18..b83de40cf 100644
--- a/nova/tests/api/openstack/test_adminapi.py
+++ b/nova/tests/api/openstack/test_adminapi.py
@@ -16,14 +16,10 @@
# under the License.
-import stubout
import webob
-from paste import urlmap
from nova import flags
from nova import test
-from nova.api import openstack
-from nova.api.openstack import auth
from nova.tests.api.openstack import fakes
FLAGS = flags.FLAGS
@@ -33,21 +29,12 @@ class AdminAPITest(test.TestCase):
def setUp(self):
super(AdminAPITest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
self.allow_admin = FLAGS.allow_admin_api
- def tearDown(self):
- self.stubs.UnsetAll()
- FLAGS.allow_admin_api = self.allow_admin
- super(AdminAPITest, self).tearDown()
-
def test_admin_enabled(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
# We should still be able to access public operations.
req = webob.Request.blank('/v1.0/flavors')
res = req.get_response(fakes.wsgi_app())
@@ -55,7 +42,7 @@ class AdminAPITest(test.TestCase):
# TODO: Confirm admin operations are available.
def test_admin_disabled(self):
- FLAGS.allow_admin_api = False
+ self.flags(allow_admin_api=False)
# We should still be able to access public operations.
req = webob.Request.blank('/v1.0/flavors')
res = req.get_response(fakes.wsgi_app())
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index af3478c7d..306ae1aa0 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -17,14 +17,12 @@
import datetime
-import stubout
import webob
import webob.dec
import nova.api
import nova.api.openstack.auth
import nova.auth.manager
-from nova import auth
from nova import context
from nova import db
from nova import test
@@ -35,7 +33,6 @@ class Test(test.TestCase):
def setUp(self):
super(Test, self).setUp()
- self.stubs = stubout.StubOutForTesting()
self.stubs.Set(nova.api.openstack.auth.AuthMiddleware,
'__init__', fakes.fake_auth_init)
self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext)
@@ -45,7 +42,6 @@ class Test(test.TestCase):
fakes.stub_out_networking(self.stubs)
def tearDown(self):
- self.stubs.UnsetAll()
fakes.fake_data_store = {}
super(Test, self).tearDown()
@@ -57,7 +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'
- result = req.get_response(fakes.wsgi_app())
+ 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)
self.assertEqual(result.headers['X-CDN-Management-Url'],
@@ -73,7 +69,7 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'user1'
req.headers['X-Auth-Key'] = 'user1_key'
- result = req.get_response(fakes.wsgi_app())
+ 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)
self.assertEqual(result.headers['X-Server-Management-Url'],
@@ -86,7 +82,7 @@ class Test(test.TestCase):
self.stubs.Set(nova.api.openstack, 'APIRouterV10', fakes.FakeRouter)
req = webob.Request.blank('/v1.0/fake')
req.headers['X-Auth-Token'] = token
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '200 OK')
self.assertEqual(result.headers['X-Test-Success'], 'True')
@@ -110,7 +106,7 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-Token'] = 'token_hash'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
self.assertEqual(self.destroy_called, True)
@@ -124,7 +120,7 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'user1'
req.headers['X-Auth-Key'] = 'user1_key'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '204 No Content')
token = result.headers['X-Auth-Token']
@@ -132,7 +128,7 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/fake')
req.headers['X-Auth-Token'] = token
req.headers['X-Auth-Project-Id'] = 'user2_project'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '200 OK')
self.assertEqual(result.headers['X-Test-Success'], 'True')
@@ -140,7 +136,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'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
def test_bad_user_good_key(self):
@@ -151,18 +147,18 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'unknown_user'
req.headers['X-Auth-Key'] = 'user1_key'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
def test_no_user(self):
req = webob.Request.blank('/v1.0/')
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
def test_bad_token(self):
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-Token'] = 'unknown_token'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
def test_bad_project(self):
@@ -177,7 +173,7 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'user1'
req.headers['X-Auth-Key'] = 'user1_key'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '204 No Content')
token = result.headers['X-Auth-Token']
@@ -185,7 +181,7 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/fake')
req.headers['X-Auth-Token'] = token
req.headers['X-Auth-Project-Id'] = 'user2_project'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
def test_not_existing_project(self):
@@ -197,7 +193,7 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'user1'
req.headers['X-Auth-Key'] = 'user1_key'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '204 No Content')
token = result.headers['X-Auth-Token']
@@ -205,7 +201,7 @@ class Test(test.TestCase):
req = webob.Request.blank('/v1.0/fake')
req.headers['X-Auth-Token'] = token
req.headers['X-Auth-Project-Id'] = 'unknown_project'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
@@ -226,20 +222,19 @@ class TestFunctional(test.TestCase):
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-Token'] = 'test_token_hash'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
def test_token_doesnotexist(self):
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-Token'] = 'nonexistant_token_hash'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '401 Unauthorized')
class TestLimiter(test.TestCase):
def setUp(self):
super(TestLimiter, self).setUp()
- self.stubs = stubout.StubOutForTesting()
self.stubs.Set(nova.api.openstack.auth.AuthMiddleware,
'__init__', fakes.fake_auth_init)
self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext)
@@ -248,7 +243,6 @@ class TestLimiter(test.TestCase):
fakes.stub_out_networking(self.stubs)
def tearDown(self):
- self.stubs.UnsetAll()
fakes.fake_data_store = {}
super(TestLimiter, self).tearDown()
@@ -261,7 +255,7 @@ class TestLimiter(test.TestCase):
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'user1'
req.headers['X-Auth-Key'] = 'user1_key'
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
token = result.headers['X-Auth-Token']
@@ -269,6 +263,6 @@ class TestLimiter(test.TestCase):
req = webob.Request.blank('/v1.0/fake')
req.method = 'POST'
req.headers['X-Auth-Token'] = token
- result = req.get_response(fakes.wsgi_app())
+ result = req.get_response(fakes.wsgi_app(fake_auth=False))
self.assertEqual(result.status, '200 OK')
self.assertEqual(result.headers['X-Test-Success'], 'True')
diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py
index 4c4d03995..0b76841f0 100644
--- a/nova/tests/api/openstack/test_common.py
+++ b/nova/tests/api/openstack/test_common.py
@@ -20,6 +20,7 @@ Test suites for 'common' code used throughout the OpenStack HTTP API.
"""
import webob.exc
+import xml.dom.minidom as minidom
from webob import Request
@@ -247,3 +248,221 @@ class MiscFunctionsTest(test.TestCase):
self.assertRaises(ValueError,
common.get_id_from_href,
fixture)
+
+ def test_get_version_from_href(self):
+ fixture = 'http://www.testsite.com/v1.1/images'
+ expected = '1.1'
+ actual = common.get_version_from_href(fixture)
+ self.assertEqual(actual, expected)
+
+ def test_get_version_from_href_2(self):
+ fixture = 'http://www.testsite.com/v1.1'
+ expected = '1.1'
+ actual = common.get_version_from_href(fixture)
+ self.assertEqual(actual, expected)
+
+ def test_get_version_from_href_default(self):
+ fixture = 'http://www.testsite.com/images'
+ expected = '1.0'
+ actual = common.get_version_from_href(fixture)
+ self.assertEqual(actual, expected)
+
+
+class MetadataXMLDeserializationTest(test.TestCase):
+
+ deserializer = common.MetadataXMLDeserializer()
+
+ def test_create(self):
+ request_body = """
+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <meta key='123'>asdf</meta>
+ <meta key='567'>jkl;</meta>
+ </metadata>"""
+ output = self.deserializer.deserialize(request_body, 'create')
+ expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}}
+ self.assertEquals(output, expected)
+
+ def test_create_empty(self):
+ request_body = """
+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"/>"""
+ output = self.deserializer.deserialize(request_body, 'create')
+ expected = {"body": {"metadata": {}}}
+ self.assertEquals(output, expected)
+
+ def test_update_all(self):
+ request_body = """
+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <meta key='123'>asdf</meta>
+ <meta key='567'>jkl;</meta>
+ </metadata>"""
+ output = self.deserializer.deserialize(request_body, 'update_all')
+ expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}}
+ self.assertEquals(output, expected)
+
+ def test_update(self):
+ request_body = """
+ <meta xmlns="http://docs.openstack.org/compute/api/v1.1"
+ key='123'>asdf</meta>"""
+ output = self.deserializer.deserialize(request_body, 'update')
+ expected = {"body": {"meta": {"123": "asdf"}}}
+ self.assertEquals(output, expected)
+
+
+class MetadataXMLSerializationTest(test.TestCase):
+
+ def test_index(self):
+ serializer = common.MetadataXMLSerializer()
+ fixture = {
+ 'metadata': {
+ 'one': 'two',
+ 'three': 'four',
+ },
+ }
+ output = serializer.serialize(fixture, 'index')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <meta key="three">
+ four
+ </meta>
+ <meta key="one">
+ two
+ </meta>
+ </metadata>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_index_null(self):
+ serializer = common.MetadataXMLSerializer()
+ fixture = {
+ 'metadata': {
+ None: None,
+ },
+ }
+ output = serializer.serialize(fixture, 'index')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <meta key="None">
+ None
+ </meta>
+ </metadata>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_index_unicode(self):
+ serializer = common.MetadataXMLSerializer()
+ fixture = {
+ 'metadata': {
+ u'three': u'Jos\xe9',
+ },
+ }
+ output = serializer.serialize(fixture, 'index')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString(u"""
+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <meta key="three">
+ Jos\xe9
+ </meta>
+ </metadata>
+ """.encode("UTF-8").replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_show(self):
+ serializer = common.MetadataXMLSerializer()
+ fixture = {
+ 'meta': {
+ 'one': 'two',
+ },
+ }
+ output = serializer.serialize(fixture, 'show')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
+ two
+ </meta>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_update_all(self):
+ serializer = common.MetadataXMLSerializer()
+ fixture = {
+ 'metadata': {
+ 'key6': 'value6',
+ 'key4': 'value4',
+ },
+ }
+ output = serializer.serialize(fixture, 'update_all')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <meta key="key6">
+ value6
+ </meta>
+ <meta key="key4">
+ value4
+ </meta>
+ </metadata>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_update_item(self):
+ serializer = common.MetadataXMLSerializer()
+ fixture = {
+ 'meta': {
+ 'one': 'two',
+ },
+ }
+ output = serializer.serialize(fixture, 'update')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
+ two
+ </meta>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_create(self):
+ serializer = common.MetadataXMLSerializer()
+ fixture = {
+ 'metadata': {
+ 'key9': 'value9',
+ 'key2': 'value2',
+ 'key1': 'value1',
+ },
+ }
+ output = serializer.serialize(fixture, 'create')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <meta key="key2">
+ value2
+ </meta>
+ <meta key="key9">
+ value9
+ </meta>
+ <meta key="key1">
+ value1
+ </meta>
+ </metadata>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_delete(self):
+ serializer = common.MetadataXMLSerializer()
+ output = serializer.serialize(None, 'delete')
+ self.assertEqual(output, '')
diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py
index 697c62e5c..47c37225c 100644
--- a/nova/tests/api/openstack/test_extensions.py
+++ b/nova/tests/api/openstack/test_extensions.py
@@ -16,13 +16,13 @@
# under the License.
import json
-import stubout
-import unittest
-import webob
import os.path
+import webob
+from xml.etree import ElementTree
from nova import context
from nova import flags
+from nova import test
from nova.api import openstack
from nova.api.openstack import extensions
from nova.api.openstack import flavors
@@ -30,7 +30,8 @@ from nova.api.openstack import wsgi
from nova.tests.api.openstack import fakes
FLAGS = flags.FLAGS
-
+NS = "{http://docs.openstack.org/compute/api/v1.1}"
+ATOMNS = "{http://www.w3.org/2005/Atom}"
response_body = "Try to say this Mr. Knox, sir..."
@@ -78,24 +79,109 @@ class StubExtensionManager(object):
return request_extensions
-class ExtensionControllerTest(unittest.TestCase):
+class ExtensionControllerTest(test.TestCase):
+
+ def setUp(self):
+ super(ExtensionControllerTest, self).setUp()
+ ext_path = os.path.join(os.path.dirname(__file__), "extensions")
+ self.flags(osapi_extensions_path=ext_path)
+
+ def test_list_extensions_json(self):
+ app = openstack.APIRouterV11()
+ ext_midware = extensions.ExtensionMiddleware(app)
+ request = webob.Request.blank("/extensions")
+ response = request.get_response(ext_midware)
+ self.assertEqual(200, response.status_int)
+
+ # Make sure we have all the extensions.
+ data = json.loads(response.body)
+ names = [x['name'] for x in data['extensions']]
+ names.sort()
+ self.assertEqual(names, ["FlavorExtraSpecs", "Floating_ips",
+ "Fox In Socks", "Hosts", "Multinic", "Volumes"])
+
+ # Make sure that at least Fox in Sox is correct.
+ (fox_ext,) = [
+ x for x in data['extensions'] if x['alias'] == 'FOXNSOX']
+ self.assertEqual(fox_ext, {
+ 'namespace': 'http://www.fox.in.socks/api/ext/pie/v1.0',
+ 'name': 'Fox In Socks',
+ 'updated': '2011-01-22T13:25:27-06:00',
+ 'description': 'The Fox In Socks Extension',
+ 'alias': 'FOXNSOX',
+ 'links': [],
+ },
+ )
+
+ def test_get_extension_json(self):
+ app = openstack.APIRouterV11()
+ ext_midware = extensions.ExtensionMiddleware(app)
+ request = webob.Request.blank("/extensions/FOXNSOX")
+ response = request.get_response(ext_midware)
+ self.assertEqual(200, response.status_int)
- def test_index(self):
+ data = json.loads(response.body)
+ self.assertEqual(data['extension'], {
+ "namespace": "http://www.fox.in.socks/api/ext/pie/v1.0",
+ "name": "Fox In Socks",
+ "updated": "2011-01-22T13:25:27-06:00",
+ "description": "The Fox In Socks Extension",
+ "alias": "FOXNSOX",
+ "links": [],
+ },
+ )
+
+ def test_list_extensions_xml(self):
app = openstack.APIRouterV11()
ext_midware = extensions.ExtensionMiddleware(app)
request = webob.Request.blank("/extensions")
+ request.accept = "application/xml"
response = request.get_response(ext_midware)
self.assertEqual(200, response.status_int)
+ print response.body
+
+ root = ElementTree.XML(response.body)
+ self.assertEqual(root.tag.split('extensions')[0], NS)
+
+ # Make sure we have all the extensions.
+ exts = root.findall('{0}extension'.format(NS))
+ self.assertEqual(len(exts), 6)
+
+ # Make sure that at least Fox in Sox is correct.
+ (fox_ext,) = [x for x in exts if x.get('alias') == 'FOXNSOX']
+ self.assertEqual(fox_ext.get('name'), 'Fox In Socks')
+ self.assertEqual(fox_ext.get('namespace'),
+ 'http://www.fox.in.socks/api/ext/pie/v1.0')
+ self.assertEqual(fox_ext.get('updated'), '2011-01-22T13:25:27-06:00')
+ self.assertEqual(fox_ext.findtext('{0}description'.format(NS)),
+ 'The Fox In Socks Extension')
- def test_get_by_alias(self):
+ def test_get_extension_xml(self):
app = openstack.APIRouterV11()
ext_midware = extensions.ExtensionMiddleware(app)
request = webob.Request.blank("/extensions/FOXNSOX")
+ request.accept = "application/xml"
response = request.get_response(ext_midware)
self.assertEqual(200, response.status_int)
+ print response.body
+ root = ElementTree.XML(response.body)
+ self.assertEqual(root.tag.split('extension')[0], NS)
+ self.assertEqual(root.get('alias'), 'FOXNSOX')
+ self.assertEqual(root.get('name'), 'Fox In Socks')
+ self.assertEqual(root.get('namespace'),
+ 'http://www.fox.in.socks/api/ext/pie/v1.0')
+ self.assertEqual(root.get('updated'), '2011-01-22T13:25:27-06:00')
+ self.assertEqual(root.findtext('{0}description'.format(NS)),
+ 'The Fox In Socks Extension')
-class ResourceExtensionTest(unittest.TestCase):
+
+class ResourceExtensionTest(test.TestCase):
+
+ def setUp(self):
+ super(ResourceExtensionTest, self).setUp()
+ ext_path = os.path.join(os.path.dirname(__file__), "extensions")
+ self.flags(osapi_extensions_path=ext_path)
def test_no_extension_present(self):
manager = StubExtensionManager(None)
@@ -133,13 +219,14 @@ class InvalidExtension(object):
return "THIRD"
-class ExtensionManagerTest(unittest.TestCase):
+class ExtensionManagerTest(test.TestCase):
response_body = "Try to say this Mr. Knox, sir..."
def setUp(self):
- FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__),
- "extensions")
+ super(ExtensionManagerTest, self).setUp()
+ ext_path = os.path.join(os.path.dirname(__file__), "extensions")
+ self.flags(osapi_extensions_path=ext_path)
def test_get_resources(self):
app = openstack.APIRouterV11()
@@ -158,11 +245,12 @@ class ExtensionManagerTest(unittest.TestCase):
self.assertTrue('THIRD' not in ext_mgr.extensions)
-class ActionExtensionTest(unittest.TestCase):
+class ActionExtensionTest(test.TestCase):
def setUp(self):
- FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__),
- "extensions")
+ super(ActionExtensionTest, self).setUp()
+ ext_path = os.path.join(os.path.dirname(__file__), "extensions")
+ self.flags(osapi_extensions_path=ext_path)
def _send_server_action_request(self, url, body):
app = openstack.APIRouterV11()
@@ -192,23 +280,16 @@ class ActionExtensionTest(unittest.TestCase):
def test_invalid_action(self):
body = dict(blah=dict(name="test"))
- response = self._send_server_action_request("/asdf/1/action", body)
+ response = self._send_server_action_request("/fdsa/1/action", body)
self.assertEqual(404, response.status_int)
-class RequestExtensionTest(unittest.TestCase):
+class RequestExtensionTest(test.TestCase):
def setUp(self):
super(RequestExtensionTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_auth(self.stubs)
- self.context = context.get_admin_context()
-
- def tearDown(self):
- self.stubs.UnsetAll()
- super(RequestExtensionTest, self).tearDown()
+ ext_path = os.path.join(os.path.dirname(__file__), "extensions")
+ self.flags(osapi_extensions_path=ext_path)
def test_get_resources_with_stub_mgr(self):
@@ -244,3 +325,109 @@ class RequestExtensionTest(unittest.TestCase):
response_data = json.loads(response.body)
self.assertEqual('newblue', response_data['flavor']['googoose'])
self.assertEqual("Pig Bands!", response_data['big_bands'])
+
+
+class ExtensionsXMLSerializerTest(test.TestCase):
+
+ def test_serialize_extenstion(self):
+ serializer = extensions.ExtensionsXMLSerializer()
+ data = {
+ 'extension': {
+ 'name': 'ext1',
+ 'namespace': 'http://docs.rack.com/servers/api/ext/pie/v1.0',
+ 'alias': 'RS-PIE',
+ 'updated': '2011-01-22T13:25:27-06:00',
+ 'description': 'Adds the capability to share an image.',
+ 'links': [
+ {
+ 'rel': 'describedby',
+ 'type': 'application/pdf',
+ 'href': 'http://docs.rack.com/servers/api/ext/cs.pdf',
+ },
+ {
+ 'rel': 'describedby',
+ 'type': 'application/vnd.sun.wadl+xml',
+ 'href': 'http://docs.rack.com/servers/api/ext/cs.wadl',
+ },
+ ],
+ },
+ }
+
+ xml = serializer.serialize(data, 'show')
+ root = ElementTree.XML(xml)
+ ext_dict = data['extension']
+ self.assertEqual(root.findtext('{0}description'.format(NS)),
+ ext_dict['description'])
+
+ for key in ['name', 'namespace', 'alias', 'updated']:
+ self.assertEqual(root.get(key), ext_dict[key])
+
+ link_nodes = root.findall('{0}link'.format(ATOMNS))
+ self.assertEqual(len(link_nodes), 2)
+ for i, link in enumerate(ext_dict['links']):
+ for key, value in link.items():
+ self.assertEqual(link_nodes[i].get(key), value)
+
+ def test_serialize_extensions(self):
+ serializer = extensions.ExtensionsXMLSerializer()
+ data = {
+ "extensions": [
+ {
+ "name": "Public Image Extension",
+ "namespace": "http://foo.com/api/ext/pie/v1.0",
+ "alias": "RS-PIE",
+ "updated": "2011-01-22T13:25:27-06:00",
+ "description": "Adds the capability to share an image.",
+ "links": [
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://foo.com/api/ext/cs-pie.pdf",
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://foo.com/api/ext/cs-pie.wadl",
+ },
+ ],
+ },
+ {
+ "name": "Cloud Block Storage",
+ "namespace": "http://foo.com/api/ext/cbs/v1.0",
+ "alias": "RS-CBS",
+ "updated": "2011-01-12T11:22:33-06:00",
+ "description": "Allows mounting cloud block storage.",
+ "links": [
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://foo.com/api/ext/cs-cbs.pdf",
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://foo.com/api/ext/cs-cbs.wadl",
+ },
+ ],
+ },
+ ],
+ }
+
+ xml = serializer.serialize(data, 'index')
+ print xml
+ root = ElementTree.XML(xml)
+ ext_elems = root.findall('{0}extension'.format(NS))
+ self.assertEqual(len(ext_elems), 2)
+ for i, ext_elem in enumerate(ext_elems):
+ ext_dict = data['extensions'][i]
+ self.assertEqual(ext_elem.findtext('{0}description'.format(NS)),
+ ext_dict['description'])
+
+ for key in ['name', 'namespace', 'alias', 'updated']:
+ self.assertEqual(ext_elem.get(key), ext_dict[key])
+
+ link_nodes = ext_elem.findall('{0}link'.format(ATOMNS))
+ self.assertEqual(len(link_nodes), 2)
+ for i, link in enumerate(ext_dict['links']):
+ for key, value in link.items():
+ self.assertEqual(link_nodes[i].get(key), value)
diff --git a/nova/tests/api/openstack/test_faults.py b/nova/tests/api/openstack/test_faults.py
index 927009e77..6da27540a 100644
--- a/nova/tests/api/openstack/test_faults.py
+++ b/nova/tests/api/openstack/test_faults.py
@@ -16,6 +16,7 @@
# under the License.
import json
+from xml.dom import minidom
import webob
import webob.dec
@@ -24,6 +25,7 @@ import webob.exc
from nova import test
from nova.api.openstack import common
from nova.api.openstack import faults
+from nova.api.openstack import wsgi
class TestFaults(test.TestCase):
@@ -144,3 +146,108 @@ class TestFaults(test.TestCase):
"""Ensure the status_int is set correctly on faults"""
fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='what?'))
self.assertEqual(fault.status_int, 400)
+
+ def test_v10_xml_serializer(self):
+ """Ensure that a v1.0 request responds with a v1.0 xmlns"""
+ request = webob.Request.blank('/',
+ headers={"Accept": "application/xml"})
+
+ fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram'))
+ response = request.get_response(fault)
+
+ self.assertTrue(common.XML_NS_V10 in response.body)
+ self.assertEqual(response.content_type, "application/xml")
+ self.assertEqual(response.status_int, 400)
+
+ def test_v11_xml_serializer(self):
+ """Ensure that a v1.1 request responds with a v1.1 xmlns"""
+ request = webob.Request.blank('/v1.1',
+ headers={"Accept": "application/xml"})
+
+ fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram'))
+ response = request.get_response(fault)
+
+ self.assertTrue(common.XML_NS_V11 in response.body)
+ self.assertEqual(response.content_type, "application/xml")
+ self.assertEqual(response.status_int, 400)
+
+
+class FaultsXMLSerializationTestV11(test.TestCase):
+ """Tests covering `nova.api.openstack.faults:Fault` class."""
+
+ def _prepare_xml(self, xml_string):
+ xml_string = xml_string.replace(" ", "")
+ xml_string = xml_string.replace("\n", "")
+ xml_string = xml_string.replace("\t", "")
+ return xml_string
+
+ def test_400_fault(self):
+ metadata = {'attributes': {"badRequest": 'code'}}
+ serializer = wsgi.XMLDictSerializer(metadata=metadata,
+ xmlns=common.XML_NS_V11)
+
+ fixture = {
+ "badRequest": {
+ "message": "scram",
+ "code": 400,
+ },
+ }
+
+ output = serializer.serialize(fixture)
+ actual = minidom.parseString(self._prepare_xml(output))
+
+ expected = minidom.parseString(self._prepare_xml("""
+ <badRequest code="400" xmlns="%s">
+ <message>scram</message>
+ </badRequest>
+ """) % common.XML_NS_V11)
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_413_fault(self):
+ metadata = {'attributes': {"overLimit": 'code'}}
+ serializer = wsgi.XMLDictSerializer(metadata=metadata,
+ xmlns=common.XML_NS_V11)
+
+ fixture = {
+ "overLimit": {
+ "message": "sorry",
+ "code": 413,
+ "retryAfter": 4,
+ },
+ }
+
+ output = serializer.serialize(fixture)
+ actual = minidom.parseString(self._prepare_xml(output))
+
+ expected = minidom.parseString(self._prepare_xml("""
+ <overLimit code="413" xmlns="%s">
+ <message>sorry</message>
+ <retryAfter>4</retryAfter>
+ </overLimit>
+ """) % common.XML_NS_V11)
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_404_fault(self):
+ metadata = {'attributes': {"itemNotFound": 'code'}}
+ serializer = wsgi.XMLDictSerializer(metadata=metadata,
+ xmlns=common.XML_NS_V11)
+
+ fixture = {
+ "itemNotFound": {
+ "message": "sorry",
+ "code": 404,
+ },
+ }
+
+ output = serializer.serialize(fixture)
+ actual = minidom.parseString(self._prepare_xml(output))
+
+ expected = minidom.parseString(self._prepare_xml("""
+ <itemNotFound code="404" xmlns="%s">
+ <message>sorry</message>
+ </itemNotFound>
+ """) % common.XML_NS_V11)
+
+ self.assertEqual(expected.toxml(), actual.toxml())
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index 4ac35b26b..d0fe72001 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -16,7 +16,6 @@
# under the License.
import json
-import stubout
import webob
import xml.dom.minidom as minidom
@@ -56,12 +55,8 @@ def return_instance_type_not_found(context, flavor_id):
class FlavorsTest(test.TestCase):
def setUp(self):
super(FlavorsTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
self.stubs.Set(nova.db.api, "instance_type_get_all",
return_instance_types)
self.stubs.Set(nova.db.api, "instance_type_get_by_flavor_id",
diff --git a/nova/tests/api/openstack/extensions/test_flavors_extra_specs.py b/nova/tests/api/openstack/test_flavors_extra_specs.py
index 2c1c335b0..d386958db 100644
--- a/nova/tests/api/openstack/extensions/test_flavors_extra_specs.py
+++ b/nova/tests/api/openstack/test_flavors_extra_specs.py
@@ -17,14 +17,13 @@
import json
import stubout
-import unittest
import webob
import os.path
from nova import flags
+from nova import test
from nova.api import openstack
-from nova.api.openstack import auth
from nova.api.openstack import extensions
from nova.tests.api.openstack import fakes
import nova.wsgi
@@ -40,10 +39,6 @@ def return_flavor_extra_specs(context, flavor_id):
return stub_flavor_extra_specs()
-def return_flavor_extra_specs(context, flavor_id):
- return stub_flavor_extra_specs()
-
-
def return_empty_flavor_extra_specs(context, flavor_id):
return {}
@@ -62,30 +57,17 @@ def stub_flavor_extra_specs():
return specs
-class FlavorsExtraSpecsTest(unittest.TestCase):
+class FlavorsExtraSpecsTest(test.TestCase):
def setUp(self):
super(FlavorsExtraSpecsTest, self).setUp()
- FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__),
- "extensions")
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.auth_data = {}
- fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_auth(self.stubs)
fakes.stub_out_key_pair_funcs(self.stubs)
- self.mware = auth.AuthMiddleware(
- extensions.ExtensionMiddleware(
- openstack.APIRouterV11()))
-
- def tearDown(self):
- self.stubs.UnsetAll()
- super(FlavorsExtraSpecsTest, self).tearDown()
def test_index(self):
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
return_flavor_extra_specs)
- request = webob.Request.blank('/flavors/1/os-extra_specs')
- res = request.get_response(self.mware)
+ request = webob.Request.blank('/v1.1/flavors/1/os-extra_specs')
+ res = request.get_response(fakes.wsgi_app())
self.assertEqual(200, res.status_int)
res_dict = json.loads(res.body)
self.assertEqual('application/json', res.headers['Content-Type'])
@@ -94,8 +76,8 @@ class FlavorsExtraSpecsTest(unittest.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('/flavors/1/os-extra_specs')
- res = req.get_response(self.mware)
+ req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs')
+ res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual('application/json', res.headers['Content-Type'])
@@ -104,8 +86,8 @@ class FlavorsExtraSpecsTest(unittest.TestCase):
def test_show(self):
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
return_flavor_extra_specs)
- req = webob.Request.blank('/flavors/1/os-extra_specs/key5')
- res = req.get_response(self.mware)
+ req = webob.Request.blank('/v1.1/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)
self.assertEqual('application/json', res.headers['Content-Type'])
@@ -114,28 +96,28 @@ class FlavorsExtraSpecsTest(unittest.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('/flavors/1/os-extra_specs/key6')
- res = req.get_response(self.mware)
+ req = webob.Request.blank('/v1.1/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)
def test_delete(self):
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_delete',
delete_flavor_extra_specs)
- req = webob.Request.blank('/flavors/1/os-extra_specs/key5')
+ req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key5')
req.method = 'DELETE'
- res = req.get_response(self.mware)
+ res = req.get_response(fakes.wsgi_app())
self.assertEqual(200, res.status_int)
def test_create(self):
self.stubs.Set(nova.db.api,
'instance_type_extra_specs_update_or_create',
return_create_flavor_extra_specs)
- req = webob.Request.blank('/flavors/1/os-extra_specs')
+ req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs')
req.method = 'POST'
req.body = '{"extra_specs": {"key1": "value1"}}'
req.headers["content-type"] = "application/json"
- res = req.get_response(self.mware)
+ res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual('application/json', res.headers['Content-Type'])
@@ -145,21 +127,21 @@ class FlavorsExtraSpecsTest(unittest.TestCase):
self.stubs.Set(nova.db.api,
'instance_type_extra_specs_update_or_create',
return_create_flavor_extra_specs)
- req = webob.Request.blank('/flavors/1/os-extra_specs')
+ req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs')
req.method = 'POST'
req.headers["content-type"] = "application/json"
- res = req.get_response(self.mware)
+ res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
def test_update_item(self):
self.stubs.Set(nova.db.api,
'instance_type_extra_specs_update_or_create',
return_create_flavor_extra_specs)
- req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
+ req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1')
req.method = 'PUT'
req.body = '{"key1": "value1"}'
req.headers["content-type"] = "application/json"
- res = req.get_response(self.mware)
+ res = req.get_response(fakes.wsgi_app())
self.assertEqual(200, res.status_int)
self.assertEqual('application/json', res.headers['Content-Type'])
res_dict = json.loads(res.body)
@@ -169,30 +151,30 @@ class FlavorsExtraSpecsTest(unittest.TestCase):
self.stubs.Set(nova.db.api,
'instance_type_extra_specs_update_or_create',
return_create_flavor_extra_specs)
- req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
+ req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1')
req.method = 'PUT'
req.headers["content-type"] = "application/json"
- res = req.get_response(self.mware)
+ res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
def test_update_item_too_many_keys(self):
self.stubs.Set(nova.db.api,
'instance_type_extra_specs_update_or_create',
return_create_flavor_extra_specs)
- req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
+ req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1')
req.method = 'PUT'
req.body = '{"key1": "value1", "key2": "value2"}'
req.headers["content-type"] = "application/json"
- res = req.get_response(self.mware)
+ res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
def test_update_item_body_uri_mismatch(self):
self.stubs.Set(nova.db.api,
'instance_type_extra_specs_update_or_create',
return_create_flavor_extra_specs)
- req = webob.Request.blank('/flavors/1/os-extra_specs/bad')
+ req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/bad')
req.method = 'PUT'
req.body = '{"key1": "value1"}'
req.headers["content-type"] = "application/json"
- res = req.get_response(self.mware)
+ res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py
index d9fb61e2a..56a0932e7 100644
--- a/nova/tests/api/openstack/test_image_metadata.py
+++ b/nova/tests/api/openstack/test_image_metadata.py
@@ -16,10 +16,7 @@
# under the License.
import json
-import stubout
-import unittest
import webob
-import xml.dom.minidom as minidom
from nova import flags
@@ -85,26 +82,15 @@ class ImageMetaDataTest(test.TestCase):
def setUp(self):
super(ImageMetaDataTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- self.orig_image_service = FLAGS.image_service
- FLAGS.image_service = 'nova.image.glance.GlanceImageService'
- fakes.FakeAuthManager.auth_data = {}
- fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_auth(self.stubs)
+ self.flags(image_service='nova.image.glance.GlanceImageService')
# NOTE(dprince) max out properties/metadata in image 3 for testing
img3 = self.IMAGE_FIXTURES[2]
for num in range(FLAGS.quota_metadata_items):
img3['properties']['key%i' % num] = "blah"
fakes.stub_out_glance(self.stubs, self.IMAGE_FIXTURES)
- def tearDown(self):
- self.stubs.UnsetAll()
- FLAGS.image_service = self.orig_image_service
- super(ImageMetaDataTest, self).tearDown()
-
def test_index(self):
- req = webob.Request.blank('/v1.1/images/1/meta')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/1/metadata')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
@@ -114,8 +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/meta/key1')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
@@ -124,42 +109,66 @@ 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/meta/key9')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/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/meta')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/2/metadata')
req.method = 'POST'
req.body = '{"metadata": {"key9": "value9"}}'
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app())
- res_dict = json.loads(res.body)
+
+ self.assertEqual(200, res.status_int)
+ actual_output = json.loads(res.body)
+
+ expected_output = {
+ 'metadata': {
+ 'key1': 'value1',
+ 'key2': 'value2',
+ 'key9': 'value9',
+ },
+ }
+
+ self.assertEqual(expected_output, actual_output)
+
+ def test_update_all(self):
+ req = webob.Request.blank('/v1.1/images/2/metadata')
+ req.method = 'PUT'
+ req.body = '{"metadata": {"key9": "value9"}}'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+
self.assertEqual(200, res.status_int)
- self.assertEqual('value9', res_dict['metadata']['key9'])
- # other items should not be modified
- self.assertEqual('value1', res_dict['metadata']['key1'])
- self.assertEqual('value2', res_dict['metadata']['key2'])
- self.assertEqual(1, len(res_dict))
+ actual_output = json.loads(res.body)
+
+ expected_output = {
+ 'metadata': {
+ 'key9': 'value9',
+ },
+ }
+
+ self.assertEqual(expected_output, actual_output)
def test_update_item(self):
- req = webob.Request.blank('/v1.1/images/1/meta/key1')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
req.method = 'PUT'
req.body = '{"meta": {"key1": "zz"}}'
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app())
+
self.assertEqual(200, res.status_int)
- res_dict = json.loads(res.body)
- self.assertTrue('meta' in res_dict)
- self.assertEqual(len(res_dict['meta']), 1)
- self.assertEqual('zz', res_dict['meta']['key1'])
+ actual_output = json.loads(res.body)
+ expected_output = {
+ 'meta': {
+ 'key1': 'zz',
+ },
+ }
+ self.assertEqual(actual_output, expected_output)
def test_update_item_bad_body(self):
- req = webob.Request.blank('/v1.1/images/1/meta/key1')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
req.method = 'PUT'
req.body = '{"key1": "zz"}'
req.headers["content-type"] = "application/json"
@@ -167,8 +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/meta/key1')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
req.method = 'PUT'
req.body = '{"meta": {"key1": "value1", "key2": "value2"}}'
req.headers["content-type"] = "application/json"
@@ -176,24 +184,38 @@ 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/meta/bad')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/1/metadata/bad')
req.method = 'PUT'
req.body = '{"meta": {"key1": "value1"}}'
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
+ def test_update_item_xml(self):
+ req = webob.Request.blank('/v1.1/images/1/metadata/key1')
+ req.method = 'PUT'
+ req.body = '<meta key="key1">five</meta>'
+ req.headers["content-type"] = "application/xml"
+ res = req.get_response(fakes.wsgi_app())
+
+ self.assertEqual(200, res.status_int)
+ actual_output = json.loads(res.body)
+ expected_output = {
+ 'meta': {
+ 'key1': 'five',
+ },
+ }
+ self.assertEqual(actual_output, expected_output)
+
def test_delete(self):
- req = webob.Request.blank('/v1.1/images/2/meta/key1')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/2/metadata/key1')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
- self.assertEqual(200, res.status_int)
+ self.assertEqual(204, res.status_int)
+ self.assertEqual('', res.body)
def test_delete_not_found(self):
- req = webob.Request.blank('/v1.1/images/2/meta/blah')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/2/metadata/blah')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(404, res.status_int)
@@ -203,8 +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/meta')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/2/metadata')
req.method = 'POST'
req.body = json_string
req.headers["content-type"] = "application/json"
@@ -212,141 +233,9 @@ class ImageMetaDataTest(test.TestCase):
self.assertEqual(400, res.status_int)
def test_too_many_metadata_items_on_put(self):
- req = webob.Request.blank('/v1.1/images/3/meta/blah')
- req.environ['api.version'] = '1.1'
+ req = webob.Request.blank('/v1.1/images/3/metadata/blah')
req.method = 'PUT'
req.body = '{"meta": {"blah": "blah"}}'
req.headers["content-type"] = "application/json"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
-
-
-class ImageMetadataXMLSerializationTest(test.TestCase):
-
- def test_index_xml(self):
- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
- fixture = {
- 'metadata': {
- 'one': 'two',
- 'three': 'four',
- },
- }
- output = serializer.serialize(fixture, 'index')
- actual = minidom.parseString(output.replace(" ", ""))
-
- expected = minidom.parseString("""
- <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
- <meta key="three">
- four
- </meta>
- <meta key="one">
- two
- </meta>
- </metadata>
- """.replace(" ", ""))
-
- self.assertEqual(expected.toxml(), actual.toxml())
-
- def test_index_xml_null(self):
- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
- fixture = {
- 'metadata': {
- None: None,
- },
- }
- output = serializer.serialize(fixture, 'index')
- actual = minidom.parseString(output.replace(" ", ""))
-
- expected = minidom.parseString("""
- <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
- <meta key="None">
- None
- </meta>
- </metadata>
- """.replace(" ", ""))
-
- self.assertEqual(expected.toxml(), actual.toxml())
-
- def test_index_xml_unicode(self):
- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
- fixture = {
- 'metadata': {
- u'three': u'Jos\xe9',
- },
- }
- output = serializer.serialize(fixture, 'index')
- actual = minidom.parseString(output.replace(" ", ""))
-
- expected = minidom.parseString(u"""
- <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
- <meta key="three">
- Jos\xe9
- </meta>
- </metadata>
- """.encode("UTF-8").replace(" ", ""))
-
- self.assertEqual(expected.toxml(), actual.toxml())
-
- def test_show_xml(self):
- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
- fixture = {
- 'meta': {
- 'one': 'two',
- },
- }
- output = serializer.serialize(fixture, 'show')
- actual = minidom.parseString(output.replace(" ", ""))
-
- expected = minidom.parseString("""
- <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
- two
- </meta>
- """.replace(" ", ""))
-
- self.assertEqual(expected.toxml(), actual.toxml())
-
- def test_update_item_xml(self):
- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
- fixture = {
- 'meta': {
- 'one': 'two',
- },
- }
- output = serializer.serialize(fixture, 'update')
- actual = minidom.parseString(output.replace(" ", ""))
-
- expected = minidom.parseString("""
- <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
- two
- </meta>
- """.replace(" ", ""))
-
- self.assertEqual(expected.toxml(), actual.toxml())
-
- def test_create_xml(self):
- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
- fixture = {
- 'metadata': {
- 'key9': 'value9',
- 'key2': 'value2',
- 'key1': 'value1',
- },
- }
- output = serializer.serialize(fixture, 'create')
- actual = minidom.parseString(output.replace(" ", ""))
-
- expected = minidom.parseString("""
- <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
- <meta key="key2">
- value2
- </meta>
- <meta key="key9">
- value9
- </meta>
- <meta key="key1">
- value1
- </meta>
- </metadata>
- """.replace(" ", ""))
-
- self.assertEqual(expected.toxml(), actual.toxml())
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 17f2fb755..8c5ad7f8d 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -155,7 +155,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests):
fakes.stub_out_compute_api_snapshot(self.stubs)
service_class = 'nova.image.glance.GlanceImageService'
self.service = utils.import_object(service_class)
- self.context = context.RequestContext(1, None)
+ self.context = context.RequestContext('fake', 'fake')
self.service.delete_all()
self.sent_to_glance = {}
fakes.stub_out_glance_add_image(self.stubs, self.sent_to_glance)
@@ -168,7 +168,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests):
"""Ensure instance_id is persisted as an image-property"""
fixture = {'name': 'test image',
'is_public': False,
- 'properties': {'instance_id': '42', 'user_id': '1'}}
+ 'properties': {'instance_id': '42', 'user_id': 'fake'}}
image_id = self.service.create(self.context, fixture)['id']
expected = fixture
@@ -178,7 +178,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests):
expected = {'id': image_id,
'name': 'test image',
'is_public': False,
- 'properties': {'instance_id': '42', 'user_id': '1'}}
+ 'properties': {'instance_id': '42', 'user_id': 'fake'}}
self.assertDictMatch(image_meta, expected)
image_metas = self.service.detail(self.context)
@@ -331,11 +331,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
self.orig_image_service = FLAGS.image_service
FLAGS.image_service = 'nova.image.glance.GlanceImageService'
self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
fakes.stub_out_key_pair_funcs(self.stubs)
self.fixtures = self._make_image_fixtures()
fakes.stub_out_glance(self.stubs, initial_fixtures=self.fixtures)
@@ -352,7 +349,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
"""Determine if this fixture is applicable for given user id."""
is_public = fixture["is_public"]
try:
- uid = int(fixture["properties"]["user_id"])
+ uid = fixture["properties"]["user_id"]
except KeyError:
uid = None
return uid == user_id or is_public
@@ -424,7 +421,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
},
"metadata": {
"instance_ref": "http://localhost/v1.1/servers/42",
- "user_id": "1",
+ "user_id": "fake",
},
"links": [{
"rel": "self",
@@ -538,7 +535,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
# because the element hasn't changed definition
expected = minidom.parseString("""
<itemNotFound code="404"
- xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ xmlns="http://docs.openstack.org/compute/api/v1.1">
<message>
Image not found.
</message>
@@ -559,7 +556,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
fixtures = copy.copy(self.fixtures)
for image in fixtures:
- if not self._applicable_fixture(image, 1):
+ if not self._applicable_fixture(image, "fake"):
fixtures.remove(image)
continue
@@ -666,7 +663,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
'name': 'queued snapshot',
'metadata': {
u'instance_ref': u'http://localhost/v1.1/servers/42',
- u'user_id': u'1',
+ u'user_id': u'fake',
},
'updated': self.NOW_API_FORMAT,
'created': self.NOW_API_FORMAT,
@@ -696,7 +693,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
'name': 'saving snapshot',
'metadata': {
u'instance_ref': u'http://localhost/v1.1/servers/42',
- u'user_id': u'1',
+ u'user_id': u'fake',
},
'updated': self.NOW_API_FORMAT,
'created': self.NOW_API_FORMAT,
@@ -727,7 +724,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
'name': 'active snapshot',
'metadata': {
u'instance_ref': u'http://localhost/v1.1/servers/42',
- u'user_id': u'1',
+ u'user_id': u'fake',
},
'updated': self.NOW_API_FORMAT,
'created': self.NOW_API_FORMAT,
@@ -757,7 +754,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
'name': 'killed snapshot',
'metadata': {
u'instance_ref': u'http://localhost/v1.1/servers/42',
- u'user_id': u'1',
+ u'user_id': u'fake',
},
'updated': self.NOW_API_FORMAT,
'created': self.NOW_API_FORMAT,
@@ -1259,7 +1256,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
# Snapshot for User 1
server_ref = 'http://localhost/v1.1/servers/42'
- snapshot_properties = {'instance_ref': server_ref, 'user_id': '1'}
+ snapshot_properties = {'instance_ref': server_ref, 'user_id': 'fake'}
for status in ('queued', 'saving', 'active', 'killed'):
add_fixture(id=image_id, name='%s snapshot' % status,
is_public=False, status=status,
@@ -1267,7 +1264,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
image_id += 1
# Snapshot for User 2
- other_snapshot_properties = {'instance_id': '43', 'user_id': '2'}
+ other_snapshot_properties = {'instance_id': '43', 'user_id': 'other'}
add_fixture(id=image_id, name='someone elses snapshot',
is_public=False, status='active',
properties=other_snapshot_properties)
diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py
index 8a3fe681a..6c3d531e3 100644
--- a/nova/tests/api/openstack/test_limits.py
+++ b/nova/tests/api/openstack/test_limits.py
@@ -920,7 +920,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
"verb": "POST",
"remaining": 2,
"unit": "MINUTE",
- "resetTime": 1311272226
+ "resetTime": 1311272226,
},
{
"URI": "*/servers",
@@ -929,7 +929,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
"verb": "POST",
"remaining": 10,
"unit": "DAY",
- "resetTime": 1311272226
+ "resetTime": 1311272226,
},
]
self.absolute_limits = {
@@ -954,7 +954,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
"verb": "POST",
"remaining": 2,
"unit": "MINUTE",
- "next-available": "2011-07-21T18:17:06Z"
+ "next-available": "2011-07-21T18:17:06Z",
},
]
},
@@ -967,7 +967,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
"verb": "POST",
"remaining": 10,
"unit": "DAY",
- "next-available": "2011-07-21T18:17:06Z"
+ "next-available": "2011-07-21T18:17:06Z",
},
]
},
@@ -989,7 +989,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
expected_limits = {
"limits": {
"rate": [],
- "absolute": {}
+ "absolute": {},
}
}
@@ -1022,7 +1022,7 @@ class LimitsXMLSerializationTest(test.TestCase):
"verb": "POST",
"remaining": 2,
"unit": "MINUTE",
- "next-available": "2011-12-15T22:42:45Z"
+ "next-available": "2011-12-15T22:42:45Z",
},
]
},
@@ -1083,7 +1083,7 @@ class LimitsXMLSerializationTest(test.TestCase):
fixture = {
"limits": {
"rate": [],
- "absolute": {}
+ "absolute": {},
}
}
diff --git a/nova/tests/api/openstack/test_server_metadata.py b/nova/tests/api/openstack/test_server_metadata.py
index 0431e68d2..f90485067 100644
--- a/nova/tests/api/openstack/test_server_metadata.py
+++ b/nova/tests/api/openstack/test_server_metadata.py
@@ -16,14 +16,12 @@
# under the License.
import json
-import stubout
-import unittest
import webob
from nova import exception
from nova import flags
-from nova.api import openstack
+from nova import test
from nova.tests.api.openstack import fakes
import nova.wsgi
@@ -76,21 +74,13 @@ def return_server_nonexistant(context, server_id):
raise exception.InstanceNotFound()
-class ServerMetaDataTest(unittest.TestCase):
+class ServerMetaDataTest(test.TestCase):
def setUp(self):
super(ServerMetaDataTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.auth_data = {}
- fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_auth(self.stubs)
fakes.stub_out_key_pair_funcs(self.stubs)
self.stubs.Set(nova.db.api, 'instance_get', return_server)
- def tearDown(self):
- self.stubs.UnsetAll()
- super(ServerMetaDataTest, self).tearDown()
-
def test_index(self):
self.stubs.Set(nova.db.api, 'instance_metadata_get',
return_server_metadata)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 1c66c1aa6..14ce42837 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -16,11 +16,11 @@
# under the License.
import base64
+import datetime
import json
import unittest
from xml.dom import minidom
-import stubout
import webob
from nova import context
@@ -30,8 +30,9 @@ from nova import flags
from nova import test
from nova import utils
import nova.api.openstack
-from nova.api.openstack import servers
from nova.api.openstack import create_instance_helper
+from nova.api.openstack import servers
+from nova.api.openstack import wsgi
import nova.compute.api
from nova.compute import instance_types
from nova.compute import power_state
@@ -77,6 +78,12 @@ def return_virtual_interface_instance_nonexistant(interfaces):
return _return_virtual_interface_by_instance
+def return_server_with_attributes(**kwargs):
+ def _return_server(context, id):
+ return stub_instance(id, **kwargs)
+ return _return_server
+
+
def return_server_with_addresses(private, public):
def _return_server(context, id):
return stub_instance(id, private_address=private,
@@ -84,20 +91,20 @@ def return_server_with_addresses(private, public):
return _return_server
-def return_server_with_interfaces(interfaces):
+def return_server_with_power_state(power_state):
def _return_server(context, id):
- return stub_instance(id, interfaces=interfaces)
+ return stub_instance(id, power_state=power_state)
return _return_server
-def return_server_with_power_state(power_state):
+def return_server_with_uuid_and_power_state(power_state):
def _return_server(context, id):
- return stub_instance(id, power_state=power_state)
+ return stub_instance(id, uuid=FAKE_UUID, power_state=power_state)
return _return_server
-def return_servers(context, user_id=1):
- return [stub_instance(i, user_id) for i in xrange(5)]
+def return_servers(context, *args, **kwargs):
+ return [stub_instance(i, 'fake', 'fake') for i in xrange(5)]
def return_servers_by_reservation(context, reservation_id=""):
@@ -140,16 +147,17 @@ def instance_addresses(context, instance_id):
return None
-def stub_instance(id, user_id=1, private_address=None, public_addresses=None,
- host=None, power_state=0, reservation_id="",
- uuid=FAKE_UUID, interfaces=None):
+def stub_instance(id, user_id='fake', project_id='fake', private_address=None,
+ public_addresses=None, host=None, power_state=0,
+ reservation_id="", uuid=FAKE_UUID, image_ref="10",
+ flavor_id="1", interfaces=None):
metadata = []
metadata.append(InstanceMetadata(key='seq', value=id))
if interfaces is None:
interfaces = []
- inst_type = instance_types.get_instance_type_by_flavor_id(1)
+ inst_type = instance_types.get_instance_type_by_flavor_id(int(flavor_id))
if public_addresses is None:
public_addresses = list()
@@ -164,10 +172,12 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None,
instance = {
"id": int(id),
+ "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0),
+ "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0),
"admin_pass": "",
"user_id": user_id,
- "project_id": "",
- "image_ref": "10",
+ "project_id": project_id,
+ "image_ref": image_ref,
"kernel_id": "",
"ramdisk_id": "",
"launch_index": 0,
@@ -223,13 +233,10 @@ class MockSetAdminPassword(object):
class ServersTest(test.TestCase):
def setUp(self):
+ self.maxDiff = None
super(ServersTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
fakes.stub_out_key_pair_funcs(self.stubs)
fakes.stub_out_image_service(self.stubs)
self.stubs.Set(utils, 'gen_uuid', fake_gen_uuid)
@@ -237,7 +244,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id)
self.stubs.Set(nova.db, 'instance_get_by_uuid',
return_server_by_uuid)
- self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
+ self.stubs.Set(nova.db.api, 'instance_get_all_by_project',
return_servers)
self.stubs.Set(nova.db.api, 'instance_add_security_group',
return_security_group)
@@ -252,15 +259,9 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.compute.API, 'resume', fake_compute_api)
self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api)
self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api)
- self.allow_admin = FLAGS.allow_admin_api
self.webreq = common.webob_factory('/v1.0/servers')
- def tearDown(self):
- self.stubs.UnsetAll()
- FLAGS.allow_admin_api = self.allow_admin
- super(ServersTest, self).tearDown()
-
def test_get_server_by_id(self):
req = webob.Request.blank('/v1.0/servers/1')
res = req.get_response(fakes.wsgi_app())
@@ -299,24 +300,346 @@ class ServersTest(test.TestCase):
self.assertEqual(res_dict['server']['name'], 'server1')
def test_get_server_by_id_v1_1(self):
+ image_bookmark = "http://localhost/images/10"
+ flavor_ref = "http://localhost/v1.1/flavors/1"
+ flavor_id = "1"
+ flavor_bookmark = "http://localhost/flavors/1"
+
+ public_ip = '192.168.0.3'
+ private_ip = '172.19.0.1'
+ interfaces = [
+ {
+ 'network': {'label': 'public'},
+ 'fixed_ips': [
+ {'address': public_ip},
+ ],
+ },
+ {
+ 'network': {'label': 'private'},
+ 'fixed_ips': [
+ {'address': private_ip},
+ ],
+ },
+ ]
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
req = webob.Request.blank('/v1.1/servers/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
- self.assertEqual(res_dict['server']['id'], 1)
- self.assertEqual(res_dict['server']['name'], 'server1')
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 0,
+ "name": "server1",
+ "status": "BUILD",
+ "hostId": '',
+ "image": {
+ "id": "10",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {
+ "public": [
+ {
+ "version": 4,
+ "addr": public_ip,
+ },
+ ],
+ "private": [
+ {
+ "version": 4,
+ "addr": private_ip,
+ },
+ ],
+ },
+ "metadata": {
+ "seq": "1",
+ },
+ "links": [
+ {
+ "rel": "self",
+ #FIXME(wwolf) Do we want the links to be id or uuid?
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ self.assertDictMatch(res_dict, expected_server)
- expected_links = [
+ def test_get_server_by_id_v1_1_xml(self):
+ image_bookmark = "http://localhost/images/10"
+ flavor_ref = "http://localhost/v1.1/flavors/1"
+ flavor_id = "1"
+ flavor_bookmark = "http://localhost/flavors/1"
+ server_href = "http://localhost/v1.1/servers/1"
+ server_bookmark = "http://localhost/servers/1"
+
+ public_ip = '192.168.0.3'
+ private_ip = '172.19.0.1'
+ interfaces = [
{
- "rel": "self",
- "href": "http://localhost/v1.1/servers/1",
+ 'network': {'label': 'public'},
+ 'fixed_ips': [
+ {'address': public_ip},
+ ],
},
{
- "rel": "bookmark",
- "href": "http://localhost/servers/1",
+ 'network': {'label': 'private'},
+ 'fixed_ips': [
+ {'address': private_ip},
+ ],
},
]
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
- self.assertEqual(res_dict['server']['links'], expected_links)
+ req = webob.Request.blank('/v1.1/servers/1')
+ req.headers['Accept'] = 'application/xml'
+ res = req.get_response(fakes.wsgi_app())
+ actual = minidom.parseString(res.body.replace(' ', ''))
+ expected_uuid = FAKE_UUID
+ expected_updated = "2010-11-11T11:00:00Z"
+ expected_created = "2010-10-10T12:00:00Z"
+ expected = minidom.parseString("""
+ <server id="1"
+ uuid="%(expected_uuid)s"
+ xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ name="server1"
+ updated="%(expected_updated)s"
+ created="%(expected_created)s"
+ hostId=""
+ status="BUILD"
+ progress="0">
+ <atom:link href="%(server_href)s" rel="self"/>
+ <atom:link href="%(server_bookmark)s" rel="bookmark"/>
+ <image id="10">
+ <atom:link rel="bookmark" href="%(image_bookmark)s"/>
+ </image>
+ <flavor id="1">
+ <atom:link rel="bookmark" href="%(flavor_bookmark)s"/>
+ </flavor>
+ <metadata>
+ <meta key="seq">
+ 1
+ </meta>
+ </metadata>
+ <addresses>
+ <network id="public">
+ <ip version="4" addr="%(public_ip)s"/>
+ </network>
+ <network id="private">
+ <ip version="4" addr="%(private_ip)s"/>
+ </network>
+ </addresses>
+ </server>
+ """.replace(" ", "") % (locals()))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_get_server_with_active_status_by_id_v1_1(self):
+ image_bookmark = "http://localhost/images/10"
+ flavor_ref = "http://localhost/v1.1/flavors/1"
+ flavor_id = "1"
+ flavor_bookmark = "http://localhost/flavors/1"
+ private_ip = "192.168.0.3"
+ public_ip = "1.2.3.4"
+
+ interfaces = [
+ {
+ 'network': {'label': 'public'},
+ 'fixed_ips': [
+ {'address': public_ip},
+ ],
+ },
+ {
+ 'network': {'label': 'private'},
+ 'fixed_ips': [
+ {'address': private_ip},
+ ],
+ },
+ ]
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces, power_state=1)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
+ req = webob.Request.blank('/v1.1/servers/1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 100,
+ "name": "server1",
+ "status": "ACTIVE",
+ "hostId": '',
+ "image": {
+ "id": "10",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {
+ "public": [
+ {
+ "version": 4,
+ "addr": public_ip,
+ },
+ ],
+ "private": [
+ {
+ "version": 4,
+ "addr": private_ip,
+ },
+ ],
+ },
+ "metadata": {
+ "seq": "1",
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ self.assertDictMatch(res_dict, expected_server)
+
+ def test_get_server_with_id_image_ref_by_id_v1_1(self):
+ image_ref = "10"
+ image_bookmark = "http://localhost/images/10"
+ flavor_ref = "http://localhost/v1.1/flavors/1"
+ flavor_id = "1"
+ flavor_bookmark = "http://localhost/flavors/1"
+ private_ip = "192.168.0.3"
+ public_ip = "1.2.3.4"
+
+ interfaces = [
+ {
+ 'network': {'label': 'public'},
+ 'fixed_ips': [
+ {'address': public_ip},
+ ],
+ },
+ {
+ 'network': {'label': 'private'},
+ 'fixed_ips': [
+ {'address': private_ip},
+ ],
+ },
+ ]
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces, power_state=1, image_ref=image_ref,
+ flavor_id=flavor_id)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
+ req = webob.Request.blank('/v1.1/servers/1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 100,
+ "name": "server1",
+ "status": "ACTIVE",
+ "hostId": '',
+ "image": {
+ "id": "10",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {
+ "public": [
+ {
+ "version": 4,
+ "addr": public_ip,
+ },
+ ],
+ "private": [
+ {
+ "version": 4,
+ "addr": private_ip,
+ },
+ ],
+ },
+ "metadata": {
+ "seq": "1",
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ self.assertDictMatch(res_dict, expected_server)
def test_get_server_by_id_with_addresses_xml(self):
private = "192.168.0.3"
@@ -451,7 +774,8 @@ class ServersTest(test.TestCase):
'fixed_ipv6': '2001:4860::12',
},
]
- new_return_server = return_server_with_interfaces(interfaces)
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
req = webob.Request.blank('/v1.1/servers/1')
@@ -494,7 +818,8 @@ class ServersTest(test.TestCase):
'fixed_ipv6': '2001:4860::12',
},
]
- new_return_server = return_server_with_interfaces(interfaces)
+ new_return_server = return_server_with_attributes(
+ interfaces=interfaces)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
req = webob.Request.blank('/v1.1/servers/1')
@@ -636,6 +961,7 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
+ self.assertEqual(len(res_dict['servers']), 5)
i = 0
for s in res_dict['servers']:
self.assertEqual(s['id'], i)
@@ -699,23 +1025,24 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
+ self.assertEqual(len(res_dict['servers']), 5)
for i, s in enumerate(res_dict['servers']):
self.assertEqual(s['id'], i)
self.assertEqual(s['name'], 'server%d' % i)
- self.assertEqual(s.get('imageId', None), None)
+ self.assertEqual(s.get('image', None), None)
expected_links = [
- {
- "rel": "self",
- "href": "http://localhost/v1.1/servers/%d" % (i,),
- },
- {
- "rel": "bookmark",
- "href": "http://localhost/servers/%d" % (i,),
- },
- ]
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/%s" % s['id'],
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/%s" % s['id'],
+ },
+ ]
- self.assertEqual(s['links'], expected_links)
+ self.assertEqual(s['links'], expected_links)
def test_get_servers_with_limit(self):
req = webob.Request.blank('/v1.0/servers?limit=3')
@@ -761,13 +1088,13 @@ class ServersTest(test.TestCase):
req = webob.Request.blank('/v1.1/servers?marker=2')
res = req.get_response(fakes.wsgi_app())
servers = json.loads(res.body)['servers']
- self.assertEqual([s['id'] for s in servers], [3, 4])
+ self.assertEqual([s['name'] for s in servers], ["server3", "server4"])
def test_get_servers_with_limit_and_marker(self):
req = webob.Request.blank('/v1.1/servers?limit=2&marker=1')
res = req.get_response(fakes.wsgi_app())
servers = json.loads(res.body)['servers']
- self.assertEqual([s['id'] for s in servers], [2, 3])
+ self.assertEqual([s['name'] for s in servers], ['server2', 'server3'])
def test_get_servers_with_bad_marker(self):
req = webob.Request.blank('/v1.1/servers?limit=2&marker=asdf')
@@ -778,8 +1105,16 @@ class ServersTest(test.TestCase):
def _setup_for_create_instance(self):
"""Shared implementation for tests below that create instance"""
def instance_create(context, inst):
- return {'id': 1, 'display_name': 'server_test',
- 'uuid': FAKE_UUID}
+ inst_type = instance_types.get_instance_type_by_flavor_id(3)
+ image_ref = 'http://localhost/images/2'
+ return {'id': 1,
+ 'display_name': 'server_test',
+ 'uuid': FAKE_UUID,
+ 'instance_type': dict(inst_type),
+ 'image_ref': image_ref,
+ "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0),
+ "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0),
+ }
def server_update(context, id, params):
return instance_create(context, id)
@@ -828,6 +1163,7 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
server = json.loads(res.body)['server']
self.assertEqual(16, len(server['adminPass']))
self.assertEqual('server_test', server['name'])
@@ -835,7 +1171,6 @@ class ServersTest(test.TestCase):
self.assertEqual(2, server['flavorId'])
self.assertEqual(3, server['imageId'])
self.assertEqual(FAKE_UUID, server['uuid'])
- self.assertEqual(res.status_int, 200)
def test_create_instance(self):
self._test_create_instance_helper()
@@ -853,7 +1188,7 @@ class ServersTest(test.TestCase):
def test_create_instance_via_zones(self):
"""Server generated ReservationID"""
self._setup_for_create_instance()
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = dict(server=dict(
name='server_test', imageId=3, flavorId=2,
@@ -875,7 +1210,7 @@ class ServersTest(test.TestCase):
def test_create_instance_via_zones_with_resid(self):
"""User supplied ReservationID"""
self._setup_for_create_instance()
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = dict(server=dict(
name='server_test', imageId=3, flavorId=2,
@@ -941,6 +1276,18 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 400)
+ def test_create_instance_no_server_entity(self):
+ self._setup_for_create_instance()
+
+ body = {}
+
+ req = webob.Request.blank('/v1.0/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 422)
+
def test_create_instance_whitespace_name(self):
self._setup_for_create_instance()
@@ -967,8 +1314,26 @@ class ServersTest(test.TestCase):
def test_create_instance_v1_1(self):
self._setup_for_create_instance()
- image_href = 'http://localhost/v1.1/images/2'
- flavor_ref = 'http://localhost/v1.1/flavors/3'
+ image_href = 'http://localhost/images/2'
+ flavor_ref = 'http://localhost/flavors/3'
+ expected_flavor = {
+ "id": "3",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/flavors/3',
+ },
+ ],
+ }
+ expected_image = {
+ "id": "2",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/images/2',
+ },
+ ],
+ }
body = {
'server': {
'name': 'server_test',
@@ -978,7 +1343,12 @@ class ServersTest(test.TestCase):
'hello': 'world',
'open': 'stack',
},
- 'personality': {},
+ 'personality': [
+ {
+ "path": "/etc/banner.txt",
+ "contents": "MQ==",
+ },
+ ],
},
}
@@ -992,10 +1362,11 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
server = json.loads(res.body)['server']
self.assertEqual(16, len(server['adminPass']))
- self.assertEqual('server_test', server['name'])
self.assertEqual(1, server['id'])
- self.assertEqual(flavor_ref, server['flavorRef'])
- self.assertEqual(image_href, server['imageRef'])
+ self.assertEqual(0, server['progress'])
+ self.assertEqual('server_test', server['name'])
+ self.assertEqual(expected_flavor, server['flavor'])
+ self.assertEqual(expected_image, server['image'])
def test_create_instance_v1_1_invalid_flavor_href(self):
self._setup_for_create_instance()
@@ -1048,8 +1419,26 @@ class ServersTest(test.TestCase):
def test_create_instance_v1_1_local_href(self):
self._setup_for_create_instance()
- image_id = 2
+ image_id = "2"
flavor_ref = 'http://localhost/v1.1/flavors/3'
+ expected_flavor = {
+ "id": "3",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/flavors/3',
+ },
+ ],
+ }
+ expected_image = {
+ "id": "2",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/images/2',
+ },
+ ],
+ }
body = {
'server': {
'name': 'server_test',
@@ -1065,11 +1454,10 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
- server = json.loads(res.body)['server']
- self.assertEqual(1, server['id'])
- self.assertEqual(flavor_ref, server['flavorRef'])
- self.assertEqual(image_id, server['imageRef'])
self.assertEqual(res.status_int, 200)
+ server = json.loads(res.body)['server']
+ self.assertEqual(expected_flavor, server['flavor'])
+ self.assertEqual(expected_image, server['image'])
def test_create_instance_with_admin_pass_v1_0(self):
self._setup_for_create_instance()
@@ -1092,7 +1480,7 @@ class ServersTest(test.TestCase):
self.assertNotEqual(res['server']['adminPass'],
body['server']['adminPass'])
- def test_create_instance_with_admin_pass_v1_1(self):
+ def test_create_instance_v1_1_admin_pass(self):
self._setup_for_create_instance()
image_href = 'http://localhost/v1.1/images/2'
@@ -1100,8 +1488,8 @@ class ServersTest(test.TestCase):
body = {
'server': {
'name': 'server_test',
- 'imageRef': image_href,
- 'flavorRef': flavor_ref,
+ 'imageRef': 3,
+ 'flavorRef': 3,
'adminPass': 'testpass',
},
}
@@ -1111,19 +1499,18 @@ class ServersTest(test.TestCase):
req.body = json.dumps(body)
req.headers['content-type'] = "application/json"
res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
server = json.loads(res.body)['server']
self.assertEqual(server['adminPass'], body['server']['adminPass'])
- def test_create_instance_with_empty_admin_pass_v1_1(self):
+ def test_create_instance_v1_1_admin_pass_empty(self):
self._setup_for_create_instance()
- image_href = 'http://localhost/v1.1/images/2'
- flavor_ref = 'http://localhost/v1.1/flavors/3'
body = {
'server': {
'name': 'server_test',
- 'imageRef': image_href,
- 'flavorRef': flavor_ref,
+ 'imageRef': 3,
+ 'flavorRef': 3,
'adminPass': '',
},
}
@@ -1293,6 +1680,24 @@ class ServersTest(test.TestCase):
self.assertEqual(s['metadata']['seq'], str(i))
def test_get_all_server_details_v1_1(self):
+ expected_flavor = {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/flavors/1',
+ },
+ ],
+ }
+ expected_image = {
+ "id": "10",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": 'http://localhost/images/10',
+ },
+ ],
+ }
req = webob.Request.blank('/v1.1/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -1301,8 +1706,8 @@ class ServersTest(test.TestCase):
self.assertEqual(s['id'], i)
self.assertEqual(s['hostId'], '')
self.assertEqual(s['name'], 'server%d' % i)
- self.assertEqual(s['imageRef'], 10)
- self.assertEqual(s['flavorRef'], 'http://localhost/v1.1/flavors/1')
+ self.assertEqual(s['image'], expected_image)
+ self.assertEqual(s['flavor'], expected_flavor)
self.assertEqual(s['status'], 'BUILD')
self.assertEqual(s['metadata']['seq'], str(i))
@@ -1314,10 +1719,11 @@ class ServersTest(test.TestCase):
instances - 2 on one host and 3 on another.
'''
- def return_servers_with_host(context, user_id=1):
- return [stub_instance(i, 1, None, None, i % 2) for i in xrange(5)]
+ def return_servers_with_host(context, *args, **kwargs):
+ return [stub_instance(i, 'fake', 'fake', None, None, i % 2)
+ for i in xrange(5)]
- self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
+ self.stubs.Set(nova.db.api, 'instance_get_all_by_project',
return_servers_with_host)
req = webob.Request.blank('/v1.0/servers/detail')
@@ -1337,7 +1743,7 @@ class ServersTest(test.TestCase):
self.assertEqual(s['flavorId'], 1)
def test_server_pause(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
@@ -1349,7 +1755,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_server_unpause(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
@@ -1361,7 +1767,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_server_suspend(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
@@ -1373,7 +1779,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_server_resume(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
@@ -1385,7 +1791,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_server_reset_network(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
@@ -1397,7 +1803,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_server_inject_network_info(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
@@ -1523,6 +1929,8 @@ class ServersTest(test.TestCase):
state = power_state.BUILDING
new_return_server = return_server_with_power_state(state)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+ self.stubs.Set(nova.db, 'instance_get_by_uuid',
+ return_server_with_uuid_and_power_state(state))
req = webob.Request.blank('/v1.0/servers/1/action')
req.method = 'POST'
@@ -1571,6 +1979,8 @@ class ServersTest(test.TestCase):
state = power_state.BUILDING
new_return_server = return_server_with_power_state(state)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+ 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.method = 'POST'
@@ -1684,7 +2094,7 @@ class ServersTest(test.TestCase):
self.assertEqual(self.server_delete_called, True)
def test_rescue_accepted(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = {}
self.called = False
@@ -1703,7 +2113,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_rescue_raises_handled(self):
- FLAGS.allow_admin_api = True
+ self.flags(allow_admin_api=True)
body = {}
def rescue_mock(*args, **kwargs):
@@ -1749,11 +2159,14 @@ class ServersTest(test.TestCase):
self.assertEqual(self.resize_called, True)
def test_resize_server_v11(self):
-
req = webob.Request.blank('/v1.1/servers/1/action')
req.content_type = 'application/json'
req.method = 'POST'
- body_dict = dict(resize=dict(flavorRef="http://localhost/3"))
+ body_dict = {
+ "resize": {
+ "flavorRef": 3,
+ },
+ }
req.body = json.dumps(body_dict)
self.resize_called = False
@@ -1767,8 +2180,8 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
self.assertEqual(self.resize_called, True)
- def test_resize_bad_flavor_fails(self):
- req = self.webreq('/1/action', 'POST', dict(resize=dict(derp=3)))
+ def test_resize_bad_flavor_data(self):
+ req = self.webreq('/1/action', 'POST', {"resize": "bad_data"})
self.resize_called = False
@@ -1778,14 +2191,54 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.compute.api.API, 'resize', resize_mock)
res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 422)
+ self.assertEqual(res.status_int, 400)
self.assertEqual(self.resize_called, False)
+ def test_resize_invalid_flavorid(self):
+ req = self.webreq('/1/action', 'POST', {"resize": {"flavorId": 300}})
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
+ def test_resize_nonint_flavorid(self):
+ req = self.webreq('/1/action', 'POST', {"resize": {"flavorId": "a"}})
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
+ def test_resize_invalid_flavorid_v1_1(self):
+ req = webob.Request.blank('/v1.1/servers/1/action')
+ req.content_type = 'application/json'
+ req.method = 'POST'
+ resize_body = {
+ "resize": {
+ "image": {
+ "id": 300,
+ },
+ },
+ }
+ req.body = json.dumps(resize_body)
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
+ def test_resize_nonint_flavorid_v1_1(self):
+ req = webob.Request.blank('/v1.1/servers/1/action')
+ req.content_type = 'application/json'
+ req.method = 'POST'
+ resize_body = {
+ "resize": {
+ "image": {
+ "id": "a",
+ },
+ },
+ }
+ req.body = json.dumps(resize_body)
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
def test_resize_raises_fails(self):
req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3)))
def resize_mock(*args):
- raise Exception('hurr durr')
+ raise Exception("An error occurred.")
self.stubs.Set(nova.compute.api.API, 'resize', resize_mock)
@@ -1823,7 +2276,7 @@ class ServersTest(test.TestCase):
req = self.webreq('/1/action', 'POST', dict(confirmResize=None))
def confirm_resize_mock(*args):
- raise Exception('hurr durr')
+ raise Exception("An error occurred.")
self.stubs.Set(nova.compute.api.API, 'confirm_resize',
confirm_resize_mock)
@@ -1850,7 +2303,7 @@ class ServersTest(test.TestCase):
req = self.webreq('/1/action', 'POST', dict(revertResize=None))
def revert_resize_mock(*args):
- raise Exception('hurr durr')
+ raise Exception("An error occurred.")
self.stubs.Set(nova.compute.api.API, 'revert_resize',
revert_resize_mock)
@@ -1894,7 +2347,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res_dict['server']['status'], 'SHUTOFF')
-class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
+class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase):
def setUp(self):
self.deserializer = create_instance_helper.ServerXMLDeserializer()
@@ -1908,6 +2361,8 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
"name": "new-server-test",
"imageId": "1",
"flavorId": "1",
+ "metadata": {},
+ "personality": [],
}}
self.assertEquals(request['body'], expected)
@@ -1923,6 +2378,7 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
"imageId": "1",
"flavorId": "1",
"metadata": {},
+ "personality": [],
}}
self.assertEquals(request['body'], expected)
@@ -1937,6 +2393,7 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
"name": "new-server-test",
"imageId": "1",
"flavorId": "1",
+ "metadata": {},
"personality": [],
}}
self.assertEquals(request['body'], expected)
@@ -2174,36 +2631,252 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""",
request = self.deserializer.deserialize(serial_request, 'create')
self.assertEqual(request['body'], expected)
- def test_request_xmlser_with_flavor_image_href(self):
+
+class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase):
+
+ def setUp(self):
+ self.deserializer = create_instance_helper.ServerXMLDeserializer()
+
+ def test_minimal_request(self):
+ serial_request = """
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ name="new-server-test"
+ imageRef="1"
+ flavorRef="2"/>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "imageRef": "1",
+ "flavorRef": "2",
+ "metadata": {},
+ "personality": [],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_admin_pass(self):
serial_request = """
- <server xmlns="http://docs.openstack.org/compute/api/v1.1"
- name="new-server-test"
- imageRef="http://localhost:8774/v1.1/images/1"
- flavorRef="http://localhost:8774/v1.1/flavors/1">
- </server>"""
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ name="new-server-test"
+ imageRef="1"
+ flavorRef="2"
+ adminPass="1234"/>"""
request = self.deserializer.deserialize(serial_request, 'create')
- self.assertEquals(request['body']["server"]["flavorRef"],
- "http://localhost:8774/v1.1/flavors/1")
- self.assertEquals(request['body']["server"]["imageRef"],
- "http://localhost:8774/v1.1/images/1")
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "imageRef": "1",
+ "flavorRef": "2",
+ "adminPass": "1234",
+ "metadata": {},
+ "personality": [],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_image_link(self):
+ serial_request = """
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ name="new-server-test"
+ imageRef="http://localhost:8774/v1.1/images/2"
+ flavorRef="3"/>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "imageRef": "http://localhost:8774/v1.1/images/2",
+ "flavorRef": "3",
+ "metadata": {},
+ "personality": [],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_flavor_link(self):
+ serial_request = """
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ name="new-server-test"
+ imageRef="1"
+ flavorRef="http://localhost:8774/v1.1/flavors/3"/>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "imageRef": "1",
+ "flavorRef": "http://localhost:8774/v1.1/flavors/3",
+ "metadata": {},
+ "personality": [],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_empty_metadata_personality(self):
+ serial_request = """
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ name="new-server-test"
+ imageRef="1"
+ flavorRef="2">
+ <metadata/>
+ <personality/>
+</server>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "imageRef": "1",
+ "flavorRef": "2",
+ "metadata": {},
+ "personality": [],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_multiple_metadata_items(self):
+ serial_request = """
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ name="new-server-test"
+ imageRef="1"
+ flavorRef="2">
+ <metadata>
+ <meta key="one">two</meta>
+ <meta key="open">snack</meta>
+ </metadata>
+</server>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "imageRef": "1",
+ "flavorRef": "2",
+ "metadata": {"one": "two", "open": "snack"},
+ "personality": [],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_multiple_personality_files(self):
+ serial_request = """
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ name="new-server-test"
+ imageRef="1"
+ flavorRef="2">
+ <personality>
+ <file path="/etc/banner.txt">MQ==</file>
+ <file path="/etc/hosts">Mg==</file>
+ </personality>
+</server>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "imageRef": "1",
+ "flavorRef": "2",
+ "metadata": {},
+ "personality": [
+ {"path": "/etc/banner.txt", "contents": "MQ=="},
+ {"path": "/etc/hosts", "contents": "Mg=="},
+ ],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_spec_request(self):
+ image_bookmark_link = "http://servers.api.openstack.org/1234/" + \
+ "images/52415800-8b69-11e0-9b19-734f6f006e54"
+ serial_request = """
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ imageRef="%s"
+ flavorRef="52415800-8b69-11e0-9b19-734f1195ff37"
+ name="new-server-test">
+ <metadata>
+ <meta key="My Server Name">Apache1</meta>
+ </metadata>
+ <personality>
+ <file path="/etc/banner.txt">Mg==</file>
+ </personality>
+</server>""" % (image_bookmark_link)
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "imageRef": "http://servers.api.openstack.org/1234/" + \
+ "images/52415800-8b69-11e0-9b19-734f6f006e54",
+ "flavorRef": "52415800-8b69-11e0-9b19-734f1195ff37",
+ "metadata": {"My Server Name": "Apache1"},
+ "personality": [
+ {
+ "path": "/etc/banner.txt",
+ "contents": "Mg==",
+ },
+ ],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+
+class TextAddressesXMLSerialization(test.TestCase):
+
+ serializer = nova.api.openstack.ips.IPXMLSerializer()
+
+ def test_show(self):
+ fixture = {
+ 'network_2': [
+ {'addr': '192.168.0.1', 'version': 4},
+ {'addr': 'fe80::beef', 'version': 6},
+ ],
+ }
+ output = self.serializer.serialize(fixture, 'show')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <network xmlns="http://docs.openstack.org/compute/api/v1.1"
+ id="network_2">
+ <ip version="4" addr="192.168.0.1"/>
+ <ip version="6" addr="fe80::beef"/>
+ </network>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_index(self):
+ fixture = {
+ 'addresses': {
+ 'network_1': [
+ {'addr': '192.168.0.3', 'version': 4},
+ {'addr': '192.168.0.5', 'version': 4},
+ ],
+ 'network_2': [
+ {'addr': '192.168.0.1', 'version': 4},
+ {'addr': 'fe80::beef', 'version': 6},
+ ],
+ },
+ }
+ output = self.serializer.serialize(fixture, 'index')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <addresses xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <network id="network_2">
+ <ip version="4" addr="192.168.0.1"/>
+ <ip version="6" addr="fe80::beef"/>
+ </network>
+ <network id="network_1">
+ <ip version="4" addr="192.168.0.3"/>
+ <ip version="4" addr="192.168.0.5"/>
+ </network>
+ </addresses>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
class TestServerInstanceCreation(test.TestCase):
def setUp(self):
super(TestServerInstanceCreation, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.auth_data = {}
- fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_auth(self.stubs)
fakes.stub_out_image_service(self.stubs)
fakes.stub_out_key_pair_funcs(self.stubs)
- self.allow_admin = FLAGS.allow_admin_api
-
- def tearDown(self):
- self.stubs.UnsetAll()
- FLAGS.allow_admin_api = self.allow_admin
- super(TestServerInstanceCreation, self).tearDown()
def _setup_mock_compute_api_for_personality(self):
@@ -2490,3 +3163,753 @@ class TestGetKernelRamdiskFromImage(test.TestCase):
kernel_id, ramdisk_id = create_instance_helper.CreateInstanceHelper. \
_do_get_kernel_ramdisk_from_image(image_meta)
return kernel_id, ramdisk_id
+
+
+class ServersViewBuilderV11Test(test.TestCase):
+
+ def setUp(self):
+ self.instance = self._get_instance()
+ self.view_builder = self._get_view_builder()
+
+ def tearDown(self):
+ pass
+
+ def _get_instance(self):
+ created_at = datetime.datetime(2010, 10, 10, 12, 0, 0)
+ updated_at = datetime.datetime(2010, 11, 11, 11, 0, 0)
+ instance = {
+ "id": 1,
+ "created_at": created_at,
+ "updated_at": updated_at,
+ "admin_pass": "",
+ "user_id": "",
+ "project_id": "",
+ "image_ref": "5",
+ "kernel_id": "",
+ "ramdisk_id": "",
+ "launch_index": 0,
+ "key_name": "",
+ "key_data": "",
+ "state": 0,
+ "state_description": "",
+ "memory_mb": 0,
+ "vcpus": 0,
+ "local_gb": 0,
+ "hostname": "",
+ "host": "",
+ "instance_type": {
+ "flavorid": 1,
+ },
+ "user_data": "",
+ "reservation_id": "",
+ "mac_address": "",
+ "scheduled_at": utils.utcnow(),
+ "launched_at": utils.utcnow(),
+ "terminated_at": utils.utcnow(),
+ "availability_zone": "",
+ "display_name": "test_server",
+ "display_description": "",
+ "locked": False,
+ "metadata": [],
+ #"address": ,
+ #"floating_ips": [{"address":ip} for ip in public_addresses]}
+ "uuid": "deadbeef-feed-edee-beef-d0ea7beefedd"}
+
+ return instance
+
+ def _get_view_builder(self):
+ base_url = "http://localhost/v1.1"
+ views = nova.api.openstack.views
+ address_builder = views.addresses.ViewBuilderV11()
+ flavor_builder = views.flavors.ViewBuilderV11(base_url)
+ image_builder = views.images.ViewBuilderV11(base_url)
+
+ view_builder = nova.api.openstack.views.servers.ViewBuilderV11(
+ address_builder,
+ flavor_builder,
+ image_builder,
+ base_url,
+ )
+ return view_builder
+
+ def test_build_server(self):
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": self.instance['uuid'],
+ "name": "test_server",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ output = self.view_builder.build(self.instance, False)
+ self.assertDictMatch(output, expected_server)
+
+ def test_build_server_detail(self):
+ image_bookmark = "http://localhost/images/5"
+ flavor_bookmark = "http://localhost/flavors/1"
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": self.instance['uuid'],
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 0,
+ "name": "test_server",
+ "status": "BUILD",
+ "hostId": '',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {},
+ "metadata": {},
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ output = self.view_builder.build(self.instance, True)
+ self.assertDictMatch(output, expected_server)
+
+ def test_build_server_detail_active_status(self):
+ #set the power state of the instance to running
+ self.instance['state'] = 1
+ image_bookmark = "http://localhost/images/5"
+ flavor_bookmark = "http://localhost/flavors/1"
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": self.instance['uuid'],
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 100,
+ "name": "test_server",
+ "status": "ACTIVE",
+ "hostId": '',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {},
+ "metadata": {},
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ output = self.view_builder.build(self.instance, True)
+ self.assertDictMatch(output, expected_server)
+
+ def test_build_server_detail_with_metadata(self):
+
+ metadata = []
+ metadata.append(InstanceMetadata(key="Open", value="Stack"))
+ metadata.append(InstanceMetadata(key="Number", value=1))
+ self.instance['metadata'] = metadata
+
+ image_bookmark = "http://localhost/images/5"
+ flavor_bookmark = "http://localhost/flavors/1"
+ expected_server = {
+ "server": {
+ "id": 1,
+ "uuid": self.instance['uuid'],
+ "updated": "2010-11-11T11:00:00Z",
+ "created": "2010-10-10T12:00:00Z",
+ "progress": 0,
+ "name": "test_server",
+ "status": "BUILD",
+ "hostId": '',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {},
+ "metadata": {
+ "Open": "Stack",
+ "Number": "1",
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/servers/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/servers/1",
+ },
+ ],
+ }
+ }
+
+ output = self.view_builder.build(self.instance, True)
+ self.assertDictMatch(output, expected_server)
+
+
+class ServerXMLSerializationTest(test.TestCase):
+
+ TIMESTAMP = "2010-10-11T10:30:22Z"
+ SERVER_HREF = 'http://localhost/v1.1/servers/123'
+ SERVER_BOOKMARK = 'http://localhost/servers/123'
+ IMAGE_BOOKMARK = 'http://localhost/images/5'
+ FLAVOR_BOOKMARK = 'http://localhost/flavors/1'
+
+ def setUp(self):
+ self.maxDiff = None
+ test.TestCase.setUp(self)
+
+ def test_show(self):
+ serializer = servers.ServerXMLSerializer()
+
+ fixture = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ 'created': self.TIMESTAMP,
+ 'updated': self.TIMESTAMP,
+ "progress": 0,
+ "name": "test_server",
+ "status": "BUILD",
+ "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": self.IMAGE_BOOKMARK,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": self.FLAVOR_BOOKMARK,
+ },
+ ],
+ },
+ "addresses": {
+ "network_one": [
+ {
+ "version": 4,
+ "addr": "67.23.10.138",
+ },
+ {
+ "version": 6,
+ "addr": "::babe:67.23.10.138",
+ },
+ ],
+ "network_two": [
+ {
+ "version": 4,
+ "addr": "67.23.10.139",
+ },
+ {
+ "version": 6,
+ "addr": "::babe:67.23.10.139",
+ },
+ ],
+ },
+ "metadata": {
+ "Open": "Stack",
+ "Number": "1",
+ },
+ 'links': [
+ {
+ 'href': self.SERVER_HREF,
+ 'rel': 'self',
+ },
+ {
+ 'href': self.SERVER_BOOKMARK,
+ 'rel': 'bookmark',
+ },
+ ],
+ }
+ }
+
+ output = serializer.serialize(fixture, 'show')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected_server_href = self.SERVER_HREF
+ expected_server_bookmark = self.SERVER_BOOKMARK
+ expected_image_bookmark = self.IMAGE_BOOKMARK
+ expected_flavor_bookmark = self.FLAVOR_BOOKMARK
+ expected_now = self.TIMESTAMP
+ expected_uuid = FAKE_UUID
+ expected = minidom.parseString("""
+ <server id="1"
+ uuid="%(expected_uuid)s"
+ xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ name="test_server"
+ updated="%(expected_now)s"
+ created="%(expected_now)s"
+ hostId="e4d909c290d0fb1ca068ffaddf22cbd0"
+ status="BUILD"
+ progress="0">
+ <atom:link href="%(expected_server_href)s" rel="self"/>
+ <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/>
+ <image id="5">
+ <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/>
+ </image>
+ <flavor id="1">
+ <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/>
+ </flavor>
+ <metadata>
+ <meta key="Open">
+ Stack
+ </meta>
+ <meta key="Number">
+ 1
+ </meta>
+ </metadata>
+ <addresses>
+ <network id="network_one">
+ <ip version="4" addr="67.23.10.138"/>
+ <ip version="6" addr="::babe:67.23.10.138"/>
+ </network>
+ <network id="network_two">
+ <ip version="4" addr="67.23.10.139"/>
+ <ip version="6" addr="::babe:67.23.10.139"/>
+ </network>
+ </addresses>
+ </server>
+ """.replace(" ", "") % (locals()))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_create(self):
+ serializer = servers.ServerXMLSerializer()
+
+ fixture = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ 'created': self.TIMESTAMP,
+ 'updated': self.TIMESTAMP,
+ "progress": 0,
+ "name": "test_server",
+ "status": "BUILD",
+ "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
+ "adminPass": "test_password",
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": self.IMAGE_BOOKMARK,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": self.FLAVOR_BOOKMARK,
+ },
+ ],
+ },
+ "addresses": {
+ "network_one": [
+ {
+ "version": 4,
+ "addr": "67.23.10.138",
+ },
+ {
+ "version": 6,
+ "addr": "::babe:67.23.10.138",
+ },
+ ],
+ "network_two": [
+ {
+ "version": 4,
+ "addr": "67.23.10.139",
+ },
+ {
+ "version": 6,
+ "addr": "::babe:67.23.10.139",
+ },
+ ],
+ },
+ "metadata": {
+ "Open": "Stack",
+ "Number": "1",
+ },
+ 'links': [
+ {
+ 'href': self.SERVER_HREF,
+ 'rel': 'self',
+ },
+ {
+ 'href': self.SERVER_BOOKMARK,
+ 'rel': 'bookmark',
+ },
+ ],
+ }
+ }
+
+ output = serializer.serialize(fixture, 'create')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected_server_href = self.SERVER_HREF
+ expected_server_bookmark = self.SERVER_BOOKMARK
+ expected_image_bookmark = self.IMAGE_BOOKMARK
+ expected_flavor_bookmark = self.FLAVOR_BOOKMARK
+ expected_now = self.TIMESTAMP
+ expected_uuid = FAKE_UUID
+ expected = minidom.parseString("""
+ <server id="1"
+ uuid="%(expected_uuid)s"
+ xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ name="test_server"
+ updated="%(expected_now)s"
+ created="%(expected_now)s"
+ hostId="e4d909c290d0fb1ca068ffaddf22cbd0"
+ status="BUILD"
+ adminPass="test_password"
+ progress="0">
+ <atom:link href="%(expected_server_href)s" rel="self"/>
+ <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/>
+ <image id="5">
+ <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/>
+ </image>
+ <flavor id="1">
+ <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/>
+ </flavor>
+ <metadata>
+ <meta key="Open">
+ Stack
+ </meta>
+ <meta key="Number">
+ 1
+ </meta>
+ </metadata>
+ <addresses>
+ <network id="network_one">
+ <ip version="4" addr="67.23.10.138"/>
+ <ip version="6" addr="::babe:67.23.10.138"/>
+ </network>
+ <network id="network_two">
+ <ip version="4" addr="67.23.10.139"/>
+ <ip version="6" addr="::babe:67.23.10.139"/>
+ </network>
+ </addresses>
+ </server>
+ """.replace(" ", "") % (locals()))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_index(self):
+ serializer = servers.ServerXMLSerializer()
+
+ expected_server_href = 'http://localhost/v1.1/servers/1'
+ expected_server_bookmark = 'http://localhost/servers/1'
+ expected_server_href_2 = 'http://localhost/v1.1/servers/2'
+ expected_server_bookmark_2 = 'http://localhost/servers/2'
+ fixture = {"servers": [
+ {
+ "id": 1,
+ "name": "test_server",
+ 'links': [
+ {
+ 'href': expected_server_href,
+ 'rel': 'self',
+ },
+ {
+ 'href': expected_server_bookmark,
+ 'rel': 'bookmark',
+ },
+ ],
+ },
+ {
+ "id": 2,
+ "name": "test_server_2",
+ 'links': [
+ {
+ 'href': expected_server_href_2,
+ 'rel': 'self',
+ },
+ {
+ 'href': expected_server_bookmark_2,
+ 'rel': 'bookmark',
+ },
+ ],
+ },
+ ]}
+
+ output = serializer.serialize(fixture, 'index')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <servers xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <server id="1" name="test_server">
+ <atom:link href="%(expected_server_href)s" rel="self"/>
+ <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/>
+ </server>
+ <server id="2" name="test_server_2">
+ <atom:link href="%(expected_server_href_2)s" rel="self"/>
+ <atom:link href="%(expected_server_bookmark_2)s" rel="bookmark"/>
+ </server>
+ </servers>
+ """.replace(" ", "") % (locals()))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_detail(self):
+ serializer = servers.ServerXMLSerializer()
+
+ expected_server_href = 'http://localhost/v1.1/servers/1'
+ expected_server_bookmark = 'http://localhost/servers/1'
+ expected_image_bookmark = self.IMAGE_BOOKMARK
+ expected_flavor_bookmark = self.FLAVOR_BOOKMARK
+ expected_now = self.TIMESTAMP
+ expected_uuid = FAKE_UUID
+
+ expected_server_href_2 = 'http://localhost/v1.1/servers/2'
+ expected_server_bookmark_2 = 'http://localhost/servers/2'
+ fixture = {"servers": [
+ {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ 'created': self.TIMESTAMP,
+ 'updated': self.TIMESTAMP,
+ "progress": 0,
+ "name": "test_server",
+ "status": "BUILD",
+ "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": expected_image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": expected_flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {
+ "network_one": [
+ {
+ "version": 4,
+ "addr": "67.23.10.138",
+ },
+ {
+ "version": 6,
+ "addr": "::babe:67.23.10.138",
+ },
+ ],
+ },
+ "metadata": {
+ "Number": "1",
+ },
+ "links": [
+ {
+ "href": expected_server_href,
+ "rel": "self",
+ },
+ {
+ "href": expected_server_bookmark,
+ "rel": "bookmark",
+ },
+ ],
+ },
+ {
+ "id": 2,
+ "uuid": FAKE_UUID,
+ 'created': self.TIMESTAMP,
+ 'updated': self.TIMESTAMP,
+ "progress": 100,
+ "name": "test_server_2",
+ "status": "ACTIVE",
+ "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0',
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": expected_image_bookmark,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": expected_flavor_bookmark,
+ },
+ ],
+ },
+ "addresses": {
+ "network_one": [
+ {
+ "version": 4,
+ "addr": "67.23.10.138",
+ },
+ {
+ "version": 6,
+ "addr": "::babe:67.23.10.138",
+ },
+ ],
+ },
+ "metadata": {
+ "Number": "2",
+ },
+ "links": [
+ {
+ "href": expected_server_href_2,
+ "rel": "self",
+ },
+ {
+ "href": expected_server_bookmark_2,
+ "rel": "bookmark",
+ },
+ ],
+ },
+ ]}
+
+ output = serializer.serialize(fixture, 'detail')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <servers xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <server id="1"
+ uuid="%(expected_uuid)s"
+ name="test_server"
+ updated="%(expected_now)s"
+ created="%(expected_now)s"
+ hostId="e4d909c290d0fb1ca068ffaddf22cbd0"
+ status="BUILD"
+ progress="0">
+ <atom:link href="%(expected_server_href)s" rel="self"/>
+ <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/>
+ <image id="5">
+ <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/>
+ </image>
+ <flavor id="1">
+ <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/>
+ </flavor>
+ <metadata>
+ <meta key="Number">
+ 1
+ </meta>
+ </metadata>
+ <addresses>
+ <network id="network_one">
+ <ip version="4" addr="67.23.10.138"/>
+ <ip version="6" addr="::babe:67.23.10.138"/>
+ </network>
+ </addresses>
+ </server>
+ <server id="2"
+ uuid="%(expected_uuid)s"
+ name="test_server_2"
+ updated="%(expected_now)s"
+ created="%(expected_now)s"
+ hostId="e4d909c290d0fb1ca068ffaddf22cbd0"
+ status="ACTIVE"
+ progress="100">
+ <atom:link href="%(expected_server_href_2)s" rel="self"/>
+ <atom:link href="%(expected_server_bookmark_2)s" rel="bookmark"/>
+ <image id="5">
+ <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/>
+ </image>
+ <flavor id="1">
+ <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/>
+ </flavor>
+ <metadata>
+ <meta key="Number">
+ 2
+ </meta>
+ </metadata>
+ <addresses>
+ <network id="network_one">
+ <ip version="4" addr="67.23.10.138"/>
+ <ip version="6" addr="::babe:67.23.10.138"/>
+ </network>
+ </addresses>
+ </server>
+ </servers>
+ """.replace(" ", "") % (locals()))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
diff --git a/nova/tests/api/openstack/test_shared_ip_groups.py b/nova/tests/api/openstack/test_shared_ip_groups.py
index c2bd7e45a..36fa1de0f 100644
--- a/nova/tests/api/openstack/test_shared_ip_groups.py
+++ b/nova/tests/api/openstack/test_shared_ip_groups.py
@@ -15,26 +15,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-import stubout
import webob
from nova import test
-from nova.api.openstack import shared_ip_groups
from nova.tests.api.openstack import fakes
class SharedIpGroupsTest(test.TestCase):
- def setUp(self):
- super(SharedIpGroupsTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_auth(self.stubs)
-
- def tearDown(self):
- self.stubs.UnsetAll()
- super(SharedIpGroupsTest, self).tearDown()
-
def test_get_shared_ip_groups(self):
req = webob.Request.blank('/v1.0/shared_ip_groups')
res = req.get_response(fakes.wsgi_app())
diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py
index effb2f592..705c02f6b 100644
--- a/nova/tests/api/openstack/test_users.py
+++ b/nova/tests/api/openstack/test_users.py
@@ -15,7 +15,6 @@
import json
-import stubout
import webob
from nova import flags
@@ -41,7 +40,7 @@ def fake_admin_check(self, req):
class UsersTest(test.TestCase):
def setUp(self):
super(UsersTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
+ self.flags(allow_admin_api=True)
self.stubs.Set(users.Controller, '__init__',
fake_init)
self.stubs.Set(users.Controller, '_check_admin',
@@ -58,16 +57,10 @@ class UsersTest(test.TestCase):
fakes.stub_out_auth(self.stubs)
self.allow_admin = FLAGS.allow_admin_api
- FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
fakemgr.add_user(User('id1', 'guy1', 'acc1', 'secret1', False))
fakemgr.add_user(User('id2', 'guy2', 'acc2', 'secret2', True))
- def tearDown(self):
- self.stubs.UnsetAll()
- FLAGS.allow_admin_api = self.allow_admin
- super(UsersTest, self).tearDown()
-
def test_get_user_list(self):
req = webob.Request.blank('/v1.0/users')
res = req.get_response(fakes.wsgi_app())
diff --git a/nova/tests/api/openstack/test_wsgi.py b/nova/tests/api/openstack/test_wsgi.py
index 5bdda7c7e..6dea78d17 100644
--- a/nova/tests/api/openstack/test_wsgi.py
+++ b/nova/tests/api/openstack/test_wsgi.py
@@ -256,6 +256,13 @@ class ResponseSerializerTest(test.TestCase):
self.assertEqual(response.body, 'pew_json')
self.assertEqual(response.status_int, 404)
+ def test_serialize_response_None(self):
+ response = self.serializer.serialize(None, 'application/json')
+ print response
+ self.assertEqual(response.headers['Content-Type'], 'application/json')
+ self.assertEqual(response.body, '')
+ self.assertEqual(response.status_int, 404)
+
def test_serialize_response_dict_to_unknown_content_type(self):
self.assertRaises(exception.InvalidContentType,
self.serializer.serialize,
diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py
index 6a6e13d93..3deb844aa 100644
--- a/nova/tests/api/openstack/test_zones.py
+++ b/nova/tests/api/openstack/test_zones.py
@@ -95,31 +95,15 @@ def zone_select(context, specs):
class ZonesTest(test.TestCase):
def setUp(self):
super(ZonesTest, self).setUp()
- self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
+ self.flags(allow_admin_api=True)
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
-
- self.allow_admin = FLAGS.allow_admin_api
- FLAGS.allow_admin_api = True
self.stubs.Set(nova.db, 'zone_get', zone_get)
self.stubs.Set(nova.db, 'zone_update', zone_update)
self.stubs.Set(nova.db, 'zone_create', zone_create)
self.stubs.Set(nova.db, 'zone_delete', zone_delete)
- self.old_zone_name = FLAGS.zone_name
- self.old_zone_capabilities = FLAGS.zone_capabilities
-
- def tearDown(self):
- self.stubs.UnsetAll()
- FLAGS.allow_admin_api = self.allow_admin
- FLAGS.zone_name = self.old_zone_name
- FLAGS.zone_capabilities = self.old_zone_capabilities
- super(ZonesTest, self).tearDown()
-
def test_get_zone_list_scheduler(self):
self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler)
req = webob.Request.blank('/v1.0/zones')
@@ -190,8 +174,8 @@ class ZonesTest(test.TestCase):
self.assertFalse('username' in res_dict['zone'])
def test_zone_info(self):
- FLAGS.zone_name = 'darksecret'
- FLAGS.zone_capabilities = ['cap1=a;b', 'cap2=c;d']
+ caps = ['cap1=a;b', 'cap2=c;d']
+ self.flags(zone_name='darksecret', zone_capabilities=caps)
self.stubs.Set(api, '_call_scheduler', zone_capabilities)
body = dict(zone=dict(username='zeb', password='sneaky'))
@@ -205,7 +189,8 @@ class ZonesTest(test.TestCase):
self.assertEqual(res_dict['zone']['cap2'], 'c;d')
def test_zone_select(self):
- FLAGS.build_plan_encryption_key = 'c286696d887c9aa0611bbb3e2025a45a'
+ key = 'c286696d887c9aa0611bbb3e2025a45a'
+ self.flags(build_plan_encryption_key=key)
self.stubs.Set(api, 'select', zone_select)
req = webob.Request.blank('/v1.0/zones/select')
diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py
index 2297d2f0e..073216495 100644
--- a/nova/tests/fake_flags.py
+++ b/nova/tests/fake_flags.py
@@ -42,3 +42,4 @@ FLAGS['iscsi_num_targets'].SetDefault(8)
FLAGS['verbose'].SetDefault(True)
FLAGS['sqlite_db'].SetDefault("tests.sqlite")
FLAGS['use_ipv6'].SetDefault(True)
+FLAGS['flat_network_bridge'].SetDefault('br100')
diff --git a/nova/tests/hyperv_unittest.py b/nova/tests/hyperv_unittest.py
index 042819b9c..0ea196950 100644
--- a/nova/tests/hyperv_unittest.py
+++ b/nova/tests/hyperv_unittest.py
@@ -23,7 +23,6 @@ from nova import context
from nova import db
from nova import flags
from nova import test
-from nova.auth import manager
from nova.virt import hyperv
FLAGS = flags.FLAGS
@@ -34,11 +33,9 @@ class HyperVTestCase(test.TestCase):
"""Test cases for the Hyper-V driver"""
def setUp(self):
super(HyperVTestCase, self).setUp()
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('fake', 'fake', 'fake',
- admin=True)
- self.project = self.manager.create_project('fake', 'fake', 'fake')
- self.context = context.RequestContext(self.user, self.project)
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
def test_create_destroy(self):
"""Create a VM and destroy it"""
diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py
index 4e8e85c7b..67b3c485a 100644
--- a/nova/tests/integrated/test_servers.py
+++ b/nova/tests/integrated/test_servers.py
@@ -305,5 +305,6 @@ class ServersTest(integrated_helpers._IntegratedTestBase):
# Cleanup
self._delete_server(server_id)
+
if __name__ == "__main__":
unittest.main()
diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py
index daea826fd..6a56a57db 100644
--- a/nova/tests/scheduler/test_scheduler.py
+++ b/nova/tests/scheduler/test_scheduler.py
@@ -23,7 +23,6 @@ import datetime
import mox
import novaclient.exceptions
import stubout
-import webob
from mox import IgnoreArg
from nova import context
@@ -34,12 +33,10 @@ from nova import service
from nova import test
from nova import rpc
from nova import utils
-from nova.auth import manager as auth_manager
from nova.scheduler import api
from nova.scheduler import manager
from nova.scheduler import driver
from nova.compute import power_state
-from nova.db.sqlalchemy import models
FLAGS = flags.FLAGS
@@ -250,23 +247,17 @@ class SimpleDriverTestCase(test.TestCase):
volume_driver='nova.volume.driver.FakeISCSIDriver',
scheduler_driver='nova.scheduler.simple.SimpleScheduler')
self.scheduler = manager.SchedulerManager()
- self.manager = auth_manager.AuthManager()
- self.user = self.manager.create_user('fake', 'fake', 'fake')
- self.project = self.manager.create_project('fake', 'fake', 'fake')
self.context = context.get_admin_context()
-
- def tearDown(self):
- self.manager.delete_user(self.user)
- self.manager.delete_project(self.project)
- super(SimpleDriverTestCase, self).tearDown()
+ self.user_id = 'fake'
+ self.project_id = 'fake'
def _create_instance(self, **kwargs):
"""Create a test instance"""
inst = {}
inst['image_id'] = 1
inst['reservation_id'] = 'r-fakeres'
- inst['user_id'] = self.user.id
- inst['project_id'] = self.project.id
+ inst['user_id'] = self.user_id
+ inst['project_id'] = self.project_id
inst['instance_type_id'] = '1'
inst['vcpus'] = kwargs.get('vcpus', 1)
inst['ami_launch_index'] = 0
@@ -485,11 +476,6 @@ class SimpleDriverTestCase(test.TestCase):
self.assertEqual(host, 'host2')
volume1.delete_volume(self.context, volume_id1)
db.volume_destroy(self.context, volume_id2)
- dic = {'service_id': s_ref['id'],
- 'vcpus': 16, 'memory_mb': 32, 'local_gb': 100,
- 'vcpus_used': 16, 'memory_mb_used': 12, 'local_gb_used': 10,
- 'hypervisor_type': 'qemu', 'hypervisor_version': 12003,
- 'cpu_info': ''}
def test_doesnt_report_disabled_hosts_as_up(self):
"""Ensures driver doesn't find hosts before they are enabled"""
diff --git a/nova/tests/scheduler/test_zone_aware_scheduler.py b/nova/tests/scheduler/test_zone_aware_scheduler.py
index d74b71fb6..7833028c3 100644
--- a/nova/tests/scheduler/test_zone_aware_scheduler.py
+++ b/nova/tests/scheduler/test_zone_aware_scheduler.py
@@ -16,6 +16,8 @@
Tests For Zone Aware Scheduler.
"""
+import json
+
import nova.db
from nova import exception
@@ -327,3 +329,19 @@ class ZoneAwareSchedulerTestCase(test.TestCase):
sched._provision_resource_from_blob(None, request_spec, 1,
request_spec, {})
self.assertTrue(was_called)
+
+ def test_decrypt_blob(self):
+ """Test that the decrypt method works."""
+
+ fixture = FakeZoneAwareScheduler()
+ test_data = {"foo": "bar"}
+
+ class StubDecryptor(object):
+ def decryptor(self, key):
+ return lambda blob: blob
+
+ self.stubs.Set(zone_aware_scheduler, 'crypto',
+ StubDecryptor())
+
+ self.assertEqual(fixture._decrypt_blob(test_data),
+ json.dumps(test_data))
diff --git a/nova/tests/test_access.py b/nova/tests/test_access.py
index e170ccee6..3b54fc249 100644
--- a/nova/tests/test_access.py
+++ b/nova/tests/test_access.py
@@ -16,7 +16,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import unittest
import webob
from nova import context
@@ -41,7 +40,7 @@ class FakeApiRequest(object):
class AccessTestCase(test.TestCase):
def _env_for(self, ctxt, action):
env = {}
- env['ec2.context'] = ctxt
+ env['nova.context'] = ctxt
env['ec2.request'] = FakeApiRequest(action)
return env
@@ -93,7 +92,11 @@ class AccessTestCase(test.TestCase):
super(AccessTestCase, self).tearDown()
def response_status(self, user, methodName):
- ctxt = context.RequestContext(user, self.project)
+ roles = manager.AuthManager().get_active_roles(user, self.project)
+ ctxt = context.RequestContext(user.id,
+ self.project.id,
+ is_admin=user.is_admin(),
+ roles=roles)
environ = self._env_for(ctxt, methodName)
req = webob.Request.blank('/', environ)
resp = req.get_response(self.mw)
@@ -105,30 +108,26 @@ class AccessTestCase(test.TestCase):
def shouldDeny(self, user, methodName):
self.assertEqual(401, self.response_status(user, methodName))
- def test_001_allow_all(self):
+ def test_allow_all(self):
users = [self.testadmin, self.testpmsys, self.testnet, self.testsys]
for user in users:
self.shouldAllow(user, '_allow_all')
- def test_002_allow_none(self):
+ def test_allow_none(self):
self.shouldAllow(self.testadmin, '_allow_none')
users = [self.testpmsys, self.testnet, self.testsys]
for user in users:
self.shouldDeny(user, '_allow_none')
- def test_003_allow_project_manager(self):
+ def test_allow_project_manager(self):
for user in [self.testadmin, self.testpmsys]:
self.shouldAllow(user, '_allow_project_manager')
for user in [self.testnet, self.testsys]:
self.shouldDeny(user, '_allow_project_manager')
- def test_004_allow_sys_and_net(self):
+ def test_allow_sys_and_net(self):
for user in [self.testadmin, self.testnet, self.testsys]:
self.shouldAllow(user, '_allow_sys_and_net')
# denied because it doesn't have the per project sysadmin
for user in [self.testpmsys]:
self.shouldDeny(user, '_allow_sys_and_net')
-
-if __name__ == "__main__":
- # TODO: Implement use_fake as an option
- unittest.main()
diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py
index 877cf4ea1..06cc498ac 100644
--- a/nova/tests/test_adminapi.py
+++ b/nova/tests/test_adminapi.py
@@ -25,7 +25,6 @@ from nova import log as logging
from nova import rpc
from nova import test
from nova import utils
-from nova.auth import manager
from nova.api.ec2 import admin
from nova.image import fake
@@ -39,7 +38,7 @@ class AdminApiTestCase(test.TestCase):
super(AdminApiTestCase, self).setUp()
self.flags(connection_type='fake')
- self.conn = rpc.Connection.instance()
+ self.conn = rpc.create_connection()
# set up our cloud
self.api = admin.AdminController()
@@ -51,11 +50,11 @@ class AdminApiTestCase(test.TestCase):
self.volume = self.start_service('volume')
self.image_service = utils.import_object(FLAGS.image_service)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('admin', 'admin', 'admin', True)
- self.project = self.manager.create_project('proj', 'admin', 'proj')
- self.context = context.RequestContext(user=self.user,
- project=self.project)
+ self.user_id = 'admin'
+ self.project_id = 'admin'
+ self.context = context.RequestContext(self.user_id,
+ self.project_id,
+ True)
def fake_show(meh, context, id):
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
@@ -73,11 +72,6 @@ class AdminApiTestCase(test.TestCase):
self.stubs.Set(rpc, 'cast', finish_cast)
- def tearDown(self):
- self.manager.delete_project(self.project)
- self.manager.delete_user(self.user)
- super(AdminApiTestCase, self).tearDown()
-
def test_block_external_ips(self):
"""Make sure provider firewall rules are created."""
result = self.api.block_external_addresses(self.context, '1.1.1.1/32')
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index e3d2ee2fc..13edcb10d 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -31,11 +31,11 @@ from nova import block_device
from nova import context
from nova import exception
from nova import test
+from nova import wsgi
from nova.api import ec2
from nova.api.ec2 import apirequest
from nova.api.ec2 import cloud
from nova.api.ec2 import ec2utils
-from nova.auth import manager
class FakeHttplibSocket(object):
@@ -195,10 +195,13 @@ class ApiEc2TestCase(test.TestCase):
"""Unit test for the cloud controller on an EC2 API"""
def setUp(self):
super(ApiEc2TestCase, self).setUp()
- self.manager = manager.AuthManager()
self.host = '127.0.0.1'
- self.app = ec2.Authenticate(ec2.Requestify(ec2.Executor(),
- 'nova.api.ec2.cloud.CloudController'))
+ # NOTE(vish): skipping the Authorizer
+ roles = ['sysadmin', 'netadmin']
+ ctxt = context.RequestContext('fake', 'fake', roles=roles)
+ self.app = wsgi.InjectContext(ctxt,
+ ec2.Requestify(ec2.Authorizer(ec2.Executor()),
+ 'nova.api.ec2.cloud.CloudController'))
def expect_http(self, host=None, is_secure=False, api_version=None):
"""Returns a new EC2 connection"""
@@ -216,7 +219,11 @@ class ApiEc2TestCase(test.TestCase):
self.http = FakeHttplibConnection(
self.app, '%s:8773' % (self.host), False)
# pylint: disable=E1103
- self.ec2.new_http_connection(host, is_secure).AndReturn(self.http)
+ if boto.Version >= '2':
+ self.ec2.new_http_connection(host or '%s:8773' % (self.host),
+ is_secure).AndReturn(self.http)
+ else:
+ self.ec2.new_http_connection(host, is_secure).AndReturn(self.http)
return self.http
def test_return_valid_isoformat(self):
@@ -245,39 +252,25 @@ class ApiEc2TestCase(test.TestCase):
self.expect_http(api_version='2010-10-30')
self.mox.ReplayAll()
- user = self.manager.create_user('fake', 'fake', 'fake')
- project = self.manager.create_project('fake', 'fake', 'fake')
-
# Any request should be fine
self.ec2.get_all_instances()
self.assertTrue(self.ec2.APIVersion in self.http.getresponsebody(),
'The version in the xmlns of the response does '
'not match the API version given in the request.')
- self.manager.delete_project(project)
- self.manager.delete_user(user)
-
def test_describe_instances(self):
"""Test that, after creating a user and a project, the describe
instances call to the API works properly"""
self.expect_http()
self.mox.ReplayAll()
- user = self.manager.create_user('fake', 'fake', 'fake')
- project = self.manager.create_project('fake', 'fake', 'fake')
self.assertEqual(self.ec2.get_all_instances(), [])
- self.manager.delete_project(project)
- self.manager.delete_user(user)
def test_terminate_invalid_instance(self):
"""Attempt to terminate an invalid instance"""
self.expect_http()
self.mox.ReplayAll()
- user = self.manager.create_user('fake', 'fake', 'fake')
- project = self.manager.create_project('fake', 'fake', 'fake')
self.assertRaises(EC2ResponseError, self.ec2.terminate_instances,
"i-00000005")
- self.manager.delete_project(project)
- self.manager.delete_user(user)
def test_get_all_key_pairs(self):
"""Test that, after creating a user and project and generating
@@ -286,16 +279,12 @@ class ApiEc2TestCase(test.TestCase):
self.mox.ReplayAll()
keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") \
for x in range(random.randint(4, 8)))
- user = self.manager.create_user('fake', 'fake', 'fake')
- project = self.manager.create_project('fake', 'fake', 'fake')
# NOTE(vish): create depends on pool, so call helper directly
- cloud._gen_key(context.get_admin_context(), user.id, keyname)
+ cloud._gen_key(context.get_admin_context(), 'fake', keyname)
rv = self.ec2.get_all_key_pairs()
results = [k for k in rv if k.name == keyname]
self.assertEquals(len(results), 1)
- self.manager.delete_project(project)
- self.manager.delete_user(user)
def test_create_duplicate_key_pair(self):
"""Test that, after successfully generating a keypair,
@@ -304,8 +293,6 @@ class ApiEc2TestCase(test.TestCase):
self.mox.ReplayAll()
keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") \
for x in range(random.randint(4, 8)))
- user = self.manager.create_user('fake', 'fake', 'fake')
- project = self.manager.create_project('fake', 'fake', 'fake')
# NOTE(vish): create depends on pool, so call helper directly
self.ec2.create_key_pair('test')
@@ -324,27 +311,16 @@ class ApiEc2TestCase(test.TestCase):
"""Test that we can retrieve security groups"""
self.expect_http()
self.mox.ReplayAll()
- user = self.manager.create_user('fake', 'fake', 'fake', admin=True)
- project = self.manager.create_project('fake', 'fake', 'fake')
rv = self.ec2.get_all_security_groups()
self.assertEquals(len(rv), 1)
self.assertEquals(rv[0].name, 'default')
- self.manager.delete_project(project)
- self.manager.delete_user(user)
-
def test_create_delete_security_group(self):
"""Test that we can create a security group"""
self.expect_http()
self.mox.ReplayAll()
- user = self.manager.create_user('fake', 'fake', 'fake', admin=True)
- project = self.manager.create_project('fake', 'fake', 'fake')
-
- # At the moment, you need both of these to actually be netadmin
- self.manager.add_role('fake', 'netadmin')
- project.add_role('fake', 'netadmin')
security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
for x in range(random.randint(4, 8)))
@@ -363,9 +339,6 @@ class ApiEc2TestCase(test.TestCase):
self.ec2.delete_security_group(security_group_name)
- self.manager.delete_project(project)
- self.manager.delete_user(user)
-
def test_authorize_revoke_security_group_cidr(self):
"""
Test that we can add and remove CIDR based rules
@@ -373,12 +346,6 @@ class ApiEc2TestCase(test.TestCase):
"""
self.expect_http()
self.mox.ReplayAll()
- user = self.manager.create_user('fake', 'fake', 'fake')
- project = self.manager.create_project('fake', 'fake', 'fake')
-
- # At the moment, you need both of these to actually be netadmin
- self.manager.add_role('fake', 'netadmin')
- project.add_role('fake', 'netadmin')
security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
for x in range(random.randint(4, 8)))
@@ -425,9 +392,6 @@ class ApiEc2TestCase(test.TestCase):
self.assertEqual(len(rv), 1)
self.assertEqual(rv[0].name, 'default')
- self.manager.delete_project(project)
- self.manager.delete_user(user)
-
return
def test_authorize_revoke_security_group_cidr_v6(self):
@@ -437,12 +401,6 @@ class ApiEc2TestCase(test.TestCase):
"""
self.expect_http()
self.mox.ReplayAll()
- user = self.manager.create_user('fake', 'fake', 'fake')
- project = self.manager.create_project('fake', 'fake', 'fake')
-
- # At the moment, you need both of these to actually be netadmin
- self.manager.add_role('fake', 'netadmin')
- project.add_role('fake', 'netadmin')
security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
for x in range(random.randint(4, 8)))
@@ -488,9 +446,6 @@ class ApiEc2TestCase(test.TestCase):
self.assertEqual(len(rv), 1)
self.assertEqual(rv[0].name, 'default')
- self.manager.delete_project(project)
- self.manager.delete_user(user)
-
return
def test_authorize_revoke_security_group_foreign_group(self):
@@ -500,12 +455,6 @@ class ApiEc2TestCase(test.TestCase):
"""
self.expect_http()
self.mox.ReplayAll()
- user = self.manager.create_user('fake', 'fake', 'fake', admin=True)
- project = self.manager.create_project('fake', 'fake', 'fake')
-
- # At the moment, you need both of these to actually be netadmin
- self.manager.add_role('fake', 'netadmin')
- project.add_role('fake', 'netadmin')
rand_string = 'sdiuisudfsdcnpaqwertasd'
security_group_name = "".join(random.choice(rand_string)
@@ -559,8 +508,3 @@ class ApiEc2TestCase(test.TestCase):
self.mox.ReplayAll()
self.ec2.delete_security_group(security_group_name)
-
- self.manager.delete_project(project)
- self.manager.delete_user(user)
-
- return
diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py
index 71e0d17c9..7c0f783bb 100644
--- a/nova/tests/test_auth.py
+++ b/nova/tests/test_auth.py
@@ -102,7 +102,7 @@ class _AuthManagerBaseTestCase(test.TestCase):
self.assertEqual('classified', u.secret)
self.assertEqual('private-party', u.access)
- def test_004_signature_is_valid(self):
+ def test_signature_is_valid(self):
with user_generator(self.manager, name='admin', secret='admin',
access='admin'):
with project_generator(self.manager, name="admin",
@@ -141,15 +141,14 @@ class _AuthManagerBaseTestCase(test.TestCase):
'127.0.0.1',
'/services/Cloud'))
- def test_005_can_get_credentials(self):
- return
- credentials = self.manager.get_user('test1').get_credentials()
- self.assertEqual(credentials,
- 'export EC2_ACCESS_KEY="access"\n' +
- 'export EC2_SECRET_KEY="secret"\n' +
- 'export EC2_URL="http://127.0.0.1:8773/services/Cloud"\n' +
- 'export S3_URL="http://127.0.0.1:3333/"\n' +
- 'export EC2_USER_ID="test1"\n')
+ def test_can_get_credentials(self):
+ st = {'access': 'access', 'secret': 'secret'}
+ with user_and_project_generator(self.manager, user_state=st) as (u, p):
+ credentials = self.manager.get_environment_rc(u, p)
+ LOG.debug(credentials)
+ self.assertTrue('export EC2_ACCESS_KEY="access:testproj"\n'
+ in credentials)
+ self.assertTrue('export EC2_SECRET_KEY="secret"\n' in credentials)
def test_can_list_users(self):
with user_generator(self.manager):
diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py
index ac959bd63..a2c8cafc0 100644
--- a/nova/tests/test_cloud.py
+++ b/nova/tests/test_cloud.py
@@ -36,7 +36,6 @@ from nova import network
from nova import rpc
from nova import test
from nova import utils
-from nova.auth import manager
from nova.api.ec2 import cloud
from nova.api.ec2 import ec2utils
from nova.image import fake
@@ -52,7 +51,7 @@ class CloudTestCase(test.TestCase):
self.flags(connection_type='fake',
stub_network=True)
- self.conn = rpc.Connection.instance()
+ self.conn = rpc.create_connection()
# set up our cloud
self.cloud = cloud.CloudController()
@@ -64,12 +63,11 @@ class CloudTestCase(test.TestCase):
self.volume = self.start_service('volume')
self.image_service = utils.import_object(FLAGS.image_service)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('admin', 'admin', 'admin', True)
- self.project = self.manager.create_project('proj', 'admin', 'proj')
- self.context = context.RequestContext(user=self.user,
- project=self.project)
- host = self.network.host
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id,
+ self.project_id,
+ True)
def fake_show(meh, context, id):
return {'id': 1, 'container_format': 'ami',
@@ -89,17 +87,15 @@ class CloudTestCase(test.TestCase):
self.stubs.Set(rpc, 'cast', finish_cast)
def tearDown(self):
- networks = db.project_get_networks(self.context, self.project.id,
+ networks = db.project_get_networks(self.context, self.project_id,
associate=False)
for network in networks:
db.network_disassociate(self.context, network['id'])
- self.manager.delete_project(self.project)
- self.manager.delete_user(self.user)
super(CloudTestCase, self).tearDown()
def _create_key(self, name):
# NOTE(vish): create depends on pool, so just call helper directly
- return cloud._gen_key(self.context, self.context.user.id, name)
+ return cloud._gen_key(self.context, self.context.user_id, name)
def test_describe_regions(self):
"""Makes sure describe regions runs without raising an exception"""
@@ -123,7 +119,6 @@ class CloudTestCase(test.TestCase):
public_ip=address)
db.floating_ip_destroy(self.context, address)
- @test.skip_test("Skipping this pending future merge")
def test_allocate_address(self):
address = "10.10.10.10"
allocate = self.cloud.allocate_address
@@ -163,13 +158,10 @@ class CloudTestCase(test.TestCase):
# ApiError: Floating ip is in use. Disassociate it before releasing.
self.assertRaises(exception.ApiError, release, self.context, address)
- @test.skip_test("Skipping this pending future merge")
def test_associate_disassociate_address(self):
"""Verifies associate runs cleanly without raising an exception"""
address = "10.10.10.10"
- db.floating_ip_create(self.context,
- {'address': address,
- 'host': self.network.host})
+ db.floating_ip_create(self.context, {'address': address})
self.cloud.allocate_address(self.context)
# TODO(jkoelker) Probably need to query for instance_type_id and
# make sure we get a valid one
@@ -177,11 +169,14 @@ class CloudTestCase(test.TestCase):
'instance_type_id': 1})
networks = db.network_get_all(self.context)
for network in networks:
- self.network.set_network_host(self.context, network['id'])
+ db.network_update(self.context, network['id'],
+ {'host': self.network.host})
project_id = self.context.project_id
type_id = inst['instance_type_id']
ips = self.network.allocate_for_instance(self.context,
instance_id=inst['id'],
+ host=inst['host'],
+ vpn=None,
instance_type_id=type_id,
project_id=project_id)
# TODO(jkoelker) Make this mas bueno
@@ -271,12 +266,61 @@ class CloudTestCase(test.TestCase):
delete = self.cloud.delete_security_group
self.assertRaises(exception.ApiError, delete, self.context)
- def test_authorize_revoke_security_group_ingress(self):
+ def test_authorize_security_group_ingress(self):
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
sec = db.security_group_create(self.context, kwargs)
authz = self.cloud.authorize_security_group_ingress
kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
- authz(self.context, group_name=sec['name'], **kwargs)
+ self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
+
+ def test_authorize_security_group_ingress_ip_permissions_ip_ranges(self):
+ kwargs = {'project_id': self.context.project_id, 'name': 'test'}
+ sec = db.security_group_create(self.context, kwargs)
+ authz = self.cloud.authorize_security_group_ingress
+ kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81,
+ 'ip_ranges':
+ {'1': {'cidr_ip': u'0.0.0.0/0'},
+ '2': {'cidr_ip': u'10.10.10.10/32'}},
+ 'ip_protocol': u'tcp'}]}
+ self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
+
+ def test_authorize_security_group_fail_missing_source_group(self):
+ kwargs = {'project_id': self.context.project_id, 'name': 'test'}
+ sec = db.security_group_create(self.context, kwargs)
+ authz = self.cloud.authorize_security_group_ingress
+ kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81,
+ 'ip_ranges':{'1': {'cidr_ip': u'0.0.0.0/0'},
+ '2': {'cidr_ip': u'10.10.10.10/32'}},
+ 'groups': {'1': {'user_id': u'someuser',
+ 'group_name': u'somegroup1'}},
+ 'ip_protocol': u'tcp'}]}
+ self.assertRaises(exception.SecurityGroupNotFound, authz,
+ self.context, group_name=sec['name'], **kwargs)
+
+ def test_authorize_security_group_ingress_ip_permissions_groups(self):
+ kwargs = {'project_id': self.context.project_id, 'name': 'test'}
+ sec = db.security_group_create(self.context,
+ {'project_id': 'someuser',
+ 'name': 'somegroup1'})
+ sec = db.security_group_create(self.context,
+ {'project_id': 'someuser',
+ 'name': 'othergroup2'})
+ sec = db.security_group_create(self.context, kwargs)
+ authz = self.cloud.authorize_security_group_ingress
+ kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81,
+ 'groups': {'1': {'user_id': u'someuser',
+ 'group_name': u'somegroup1'},
+ '2': {'user_id': u'someuser',
+ 'group_name': u'othergroup2'}},
+ 'ip_protocol': u'tcp'}]}
+ self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
+
+ def test_revoke_security_group_ingress(self):
+ kwargs = {'project_id': self.context.project_id, 'name': 'test'}
+ sec = db.security_group_create(self.context, kwargs)
+ authz = self.cloud.authorize_security_group_ingress
+ kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
+ authz(self.context, group_id=sec['id'], **kwargs)
revoke = self.cloud.revoke_security_group_ingress
self.assertTrue(revoke(self.context, group_name=sec['name'], **kwargs))
@@ -368,8 +412,6 @@ class CloudTestCase(test.TestCase):
db.service_destroy(self.context, service1['id'])
db.service_destroy(self.context, service2['id'])
- # NOTE(jkoelker): this test relies on fixed_ip being in instances
- @test.skip_test("EC2 stuff needs fixed_ip in instance_ref")
def test_describe_snapshots(self):
"""Makes sure describe_snapshots works and filters results."""
vol = db.volume_create(self.context, {})
@@ -941,7 +983,7 @@ class CloudTestCase(test.TestCase):
key = RSA.load_key_string(private_key, callback=lambda: None)
bio = BIO.MemoryBuffer()
public_key = db.key_pair_get(self.context,
- self.context.user.id,
+ self.context.user_id,
'test')['public_key']
key.save_pub_key_bio(bio)
converted = crypto.ssl_pub_to_ssh_pub(bio.read())
@@ -965,7 +1007,7 @@ class CloudTestCase(test.TestCase):
'mytestfprint')
self.assertTrue(result1)
keydata = db.key_pair_get(self.context,
- self.context.user.id,
+ self.context.user_id,
'testimportkey1')
self.assertEqual('mytestpubkey', keydata['public_key'])
self.assertEqual('mytestfprint', keydata['fingerprint'])
@@ -982,7 +1024,7 @@ class CloudTestCase(test.TestCase):
dummypub)
self.assertTrue(result2)
keydata = db.key_pair_get(self.context,
- self.context.user.id,
+ self.context.user_id,
'testimportkey2')
self.assertEqual(dummypub, keydata['public_key'])
self.assertEqual(dummyfprint, keydata['fingerprint'])
@@ -992,12 +1034,6 @@ class CloudTestCase(test.TestCase):
self.cloud.delete_key_pair(self.context, 'test')
def test_run_instances(self):
- # stub out the rpc call
- def stub_cast(*args, **kwargs):
- pass
-
- self.stubs.Set(rpc, 'cast', stub_cast)
-
kwargs = {'image_id': FLAGS.default_image,
'instance_type': FLAGS.default_instance_type,
'max_count': 1}
@@ -1007,7 +1043,7 @@ class CloudTestCase(test.TestCase):
self.assertEqual(instance['imageId'], 'ami-00000001')
self.assertEqual(instance['displayName'], 'Server 1')
self.assertEqual(instance['instanceId'], 'i-00000001')
- self.assertEqual(instance['instanceState']['name'], 'scheduling')
+ self.assertEqual(instance['instanceState']['name'], 'running')
self.assertEqual(instance['instanceType'], 'm1.small')
def test_run_instances_image_state_none(self):
@@ -1079,16 +1115,15 @@ class CloudTestCase(test.TestCase):
self.assertEqual('c00l 1m4g3', inst['display_name'])
db.instance_destroy(self.context, inst['id'])
- # NOTE(jkoelker): This test relies on mac_address in instance
- @test.skip_test("EC2 stuff needs mac_address in instance_ref")
def test_update_of_instance_wont_update_private_fields(self):
inst = db.instance_create(self.context, {})
+ host = inst['host']
ec2_id = ec2utils.id_to_ec2_id(inst['id'])
self.cloud.update_instance(self.context, ec2_id,
display_name='c00l 1m4g3',
- mac_address='DE:AD:BE:EF')
+ host='otherhost')
inst = db.instance_get(self.context, inst['id'])
- self.assertEqual(None, inst['mac_address'])
+ self.assertEqual(host, inst['host'])
db.instance_destroy(self.context, inst['id'])
def test_update_of_volume_display_fields(self):
@@ -1144,7 +1179,6 @@ class CloudTestCase(test.TestCase):
elevated = self.context.elevated(read_deleted=True)
self._wait_for_state(elevated, instance_id, is_deleted)
- @test.skip_test("skipping, test is hanging with multinic for rpc reasons")
def test_stop_start_instance(self):
"""Makes sure stop/start instance works"""
# enforce periodic tasks run in short time to avoid wait for 60s.
@@ -1202,7 +1236,6 @@ class CloudTestCase(test.TestCase):
self.assertEqual(vol['status'], "available")
self.assertEqual(vol['attach_status'], "detached")
- @test.skip_test("skipping, test is hanging with multinic for rpc reasons")
def test_stop_start_with_volume(self):
"""Make sure run instance with block device mapping works"""
@@ -1271,7 +1304,6 @@ class CloudTestCase(test.TestCase):
self._restart_compute_service()
- @test.skip_test("skipping, test is hanging with multinic for rpc reasons")
def test_stop_with_attached_volume(self):
"""Make sure attach info is reflected to block device mapping"""
# enforce periodic tasks run in short time to avoid wait for 60s.
@@ -1347,7 +1379,6 @@ class CloudTestCase(test.TestCase):
greenthread.sleep(0.3)
return result['snapshotId']
- @test.skip_test("skipping, test is hanging with multinic for rpc reasons")
def test_run_with_snapshot(self):
"""Makes sure run/stop/start instance with snapshot works."""
vol = self._volume_create()
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 32f55c6e2..24cfbd511 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -19,10 +19,6 @@
Tests For Compute
"""
-import mox
-import stubout
-
-from nova.auth import manager
from nova import compute
from nova.compute import instance_types
from nova.compute import manager as compute_manager
@@ -67,10 +63,9 @@ class ComputeTestCase(test.TestCase):
network_manager='nova.network.manager.FlatManager')
self.compute = utils.import_object(FLAGS.compute_manager)
self.compute_api = compute.API()
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('fake', 'fake', 'fake')
- self.project = self.manager.create_project('fake', 'fake', 'fake')
- self.context = context.RequestContext('fake', 'fake', False)
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
test_notifier.NOTIFICATIONS = []
def fake_show(meh, context, id):
@@ -78,19 +73,14 @@ class ComputeTestCase(test.TestCase):
self.stubs.Set(nova.image.fake._FakeImageService, 'show', fake_show)
- def tearDown(self):
- self.manager.delete_user(self.user)
- self.manager.delete_project(self.project)
- super(ComputeTestCase, self).tearDown()
-
def _create_instance(self, params={}):
"""Create a test instance"""
inst = {}
inst['image_ref'] = 1
inst['reservation_id'] = 'r-fakeres'
inst['launch_time'] = '10'
- inst['user_id'] = self.user.id
- inst['project_id'] = self.project.id
+ inst['user_id'] = self.user_id
+ inst['project_id'] = self.project_id
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
inst['instance_type_id'] = type_id
inst['ami_launch_index'] = 0
@@ -115,8 +105,8 @@ class ComputeTestCase(test.TestCase):
def _create_group(self):
values = {'name': 'testgroup',
'description': 'testgroup',
- 'user_id': self.user.id,
- 'project_id': self.project.id}
+ 'user_id': self.user_id,
+ 'project_id': self.project_id}
return db.security_group_create(self.context, values)
def _get_dummy_instance(self):
@@ -350,8 +340,8 @@ 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['user_id'], self.user.id)
+ self.assertEquals(payload['tenant_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')
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
@@ -374,8 +364,8 @@ 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['user_id'], self.user.id)
+ self.assertEquals(payload['tenant_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')
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
@@ -420,7 +410,7 @@ class ComputeTestCase(test.TestCase):
def fake(*args, **kwargs):
pass
- self.stubs.Set(self.compute.driver, 'finish_resize', fake)
+ self.stubs.Set(self.compute.driver, 'finish_migration', fake)
self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake)
context = self.context.elevated()
instance_id = self._create_instance()
@@ -457,8 +447,8 @@ 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['user_id'], self.user.id)
+ self.assertEquals(payload['tenant_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')
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
@@ -531,8 +521,8 @@ class ComputeTestCase(test.TestCase):
def fake(*args, **kwargs):
pass
- self.stubs.Set(self.compute.driver, 'finish_resize', fake)
- self.stubs.Set(self.compute.driver, 'revert_resize', fake)
+ self.stubs.Set(self.compute.driver, 'finish_migration', fake)
+ self.stubs.Set(self.compute.driver, 'revert_migration', fake)
self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake)
self.compute.run_instance(self.context, instance_id)
@@ -583,8 +573,9 @@ class ComputeTestCase(test.TestCase):
the same host"""
instance_id = self._create_instance()
self.compute.run_instance(self.context, instance_id)
+ inst_ref = db.instance_get(self.context, instance_id)
self.assertRaises(exception.Error, self.compute.prep_resize,
- self.context, instance_id, 1)
+ self.context, inst_ref['uuid'], 1)
self.compute.terminate_instance(self.context, instance_id)
def test_migrate(self):
@@ -624,7 +615,6 @@ class ComputeTestCase(test.TestCase):
self._setup_other_managers()
dbmock = self.mox.CreateMock(db)
volmock = self.mox.CreateMock(self.volume_manager)
- netmock = self.mox.CreateMock(self.network_manager)
drivermock = self.mox.CreateMock(self.compute_driver)
dbmock.instance_get(c, i_ref['id']).AndReturn(i_ref)
@@ -632,12 +622,11 @@ class ComputeTestCase(test.TestCase):
for i in range(len(i_ref['volumes'])):
vid = i_ref['volumes'][i]['id']
volmock.setup_compute_volume(c, vid).InAnyOrder('g1')
- netmock.setup_compute_network(c, i_ref['id'])
+ drivermock.plug_vifs(i_ref, [])
drivermock.ensure_filtering_rules_for_instance(i_ref)
self.compute.db = dbmock
self.compute.volume_manager = volmock
- self.compute.network_manager = netmock
self.compute.driver = drivermock
self.mox.ReplayAll()
@@ -652,18 +641,16 @@ class ComputeTestCase(test.TestCase):
self._setup_other_managers()
dbmock = self.mox.CreateMock(db)
- netmock = self.mox.CreateMock(self.network_manager)
drivermock = self.mox.CreateMock(self.compute_driver)
dbmock.instance_get(c, i_ref['id']).AndReturn(i_ref)
dbmock.instance_get_fixed_addresses(c, i_ref['id']).AndReturn('dummy')
self.mox.StubOutWithMock(compute_manager.LOG, 'info')
compute_manager.LOG.info(_("%s has no volume."), i_ref['hostname'])
- netmock.setup_compute_network(c, i_ref['id'])
+ drivermock.plug_vifs(i_ref, [])
drivermock.ensure_filtering_rules_for_instance(i_ref)
self.compute.db = dbmock
- self.compute.network_manager = netmock
self.compute.driver = drivermock
self.mox.ReplayAll()
@@ -684,18 +671,20 @@ class ComputeTestCase(test.TestCase):
dbmock = self.mox.CreateMock(db)
netmock = self.mox.CreateMock(self.network_manager)
volmock = self.mox.CreateMock(self.volume_manager)
+ drivermock = self.mox.CreateMock(self.compute_driver)
dbmock.instance_get(c, i_ref['id']).AndReturn(i_ref)
dbmock.instance_get_fixed_addresses(c, i_ref['id']).AndReturn('dummy')
for i in range(len(i_ref['volumes'])):
volmock.setup_compute_volume(c, i_ref['volumes'][i]['id'])
for i in range(FLAGS.live_migration_retry_count):
- netmock.setup_compute_network(c, i_ref['id']).\
+ drivermock.plug_vifs(i_ref, []).\
AndRaise(exception.ProcessExecutionError())
self.compute.db = dbmock
self.compute.network_manager = netmock
self.compute.volume_manager = volmock
+ self.compute.driver = drivermock
self.mox.ReplayAll()
self.assertRaises(exception.ProcessExecutionError,
@@ -830,7 +819,7 @@ class ComputeTestCase(test.TestCase):
for v in i_ref['volumes']:
self.compute.volume_manager.remove_compute_volume(c, v['id'])
self.mox.StubOutWithMock(self.compute.driver, 'unfilter_instance')
- self.compute.driver.unfilter_instance(i_ref)
+ self.compute.driver.unfilter_instance(i_ref, [])
# executing
self.mox.ReplayAll()
@@ -850,7 +839,6 @@ class ComputeTestCase(test.TestCase):
def test_run_kill_vm(self):
"""Detect when a vm is terminated behind the scenes"""
- self.stubs = stubout.StubOutForTesting()
self.stubs.Set(compute_manager.ComputeManager,
'_report_driver_status', nop_report_driver_status)
diff --git a/nova/tests/test_console.py b/nova/tests/test_console.py
index 1806cc1ea..cf7f592cf 100644
--- a/nova/tests/test_console.py
+++ b/nova/tests/test_console.py
@@ -26,10 +26,9 @@ from nova import exception
from nova import flags
from nova import test
from nova import utils
-from nova.auth import manager
-from nova.console import manager as console_manager
FLAGS = flags.FLAGS
+flags.DECLARE('console_driver', 'nova.console.manager')
class ConsoleTestCase(test.TestCase):
@@ -39,17 +38,11 @@ class ConsoleTestCase(test.TestCase):
self.flags(console_driver='nova.console.fake.FakeConsoleProxy',
stub_compute=True)
self.console = utils.import_object(FLAGS.console_manager)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('fake', 'fake', 'fake')
- self.project = self.manager.create_project('fake', 'fake', 'fake')
- self.context = context.get_admin_context()
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
self.host = 'test_compute_host'
- def tearDown(self):
- self.manager.delete_user(self.user)
- self.manager.delete_project(self.project)
- super(ConsoleTestCase, self).tearDown()
-
def _create_instance(self):
"""Create a test instance"""
inst = {}
@@ -58,8 +51,8 @@ class ConsoleTestCase(test.TestCase):
inst['image_id'] = 1
inst['reservation_id'] = 'r-fakeres'
inst['launch_time'] = '10'
- inst['user_id'] = self.user.id
- inst['project_id'] = self.project.id
+ inst['user_id'] = self.user_id
+ inst['project_id'] = self.project_id
inst['instance_type_id'] = 1
inst['ami_launch_index'] = 0
return db.instance_create(self.context, inst)['id']
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
new file mode 100644
index 000000000..54448f9d6
--- /dev/null
+++ b/nova/tests/test_db_api.py
@@ -0,0 +1,78 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Unit tests for the DB API"""
+
+from nova import test
+from nova import context
+from nova import db
+from nova import flags
+
+FLAGS = flags.FLAGS
+
+
+def _setup_networking(instance_id, ip='1.2.3.4', flo_addr='1.2.1.2'):
+ ctxt = context.get_admin_context()
+ network_ref = db.project_get_networks(ctxt,
+ 'fake',
+ associate=True)[0]
+ vif = {'address': '56:12:12:12:12:12',
+ 'network_id': network_ref['id'],
+ 'instance_id': instance_id}
+ vif_ref = db.virtual_interface_create(ctxt, vif)
+
+ fixed_ip = {'address': ip,
+ 'network_id': network_ref['id'],
+ 'virtual_interface_id': vif_ref['id'],
+ 'allocated': True,
+ 'instance_id': instance_id}
+ db.fixed_ip_create(ctxt, fixed_ip)
+ fix_ref = db.fixed_ip_get_by_address(ctxt, ip)
+ db.floating_ip_create(ctxt, {'address': flo_addr,
+ 'fixed_ip_id': fix_ref['id']})
+
+
+class DbApiTestCase(test.TestCase):
+ def setUp(self):
+ super(DbApiTestCase, self).setUp()
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
+
+ def test_instance_get_project_vpn(self):
+ values = {'instance_type_id': FLAGS.default_instance_type,
+ 'image_ref': FLAGS.vpn_image_id,
+ 'project_id': self.project_id
+ }
+ instance = db.instance_create(self.context, values)
+ result = db.instance_get_project_vpn(self.context.elevated(),
+ self.project_id)
+ self.assertEqual(instance['id'], result['id'])
+
+ def test_instance_get_project_vpn_joins(self):
+ values = {'instance_type_id': FLAGS.default_instance_type,
+ 'image_ref': FLAGS.vpn_image_id,
+ 'project_id': self.project_id
+ }
+ instance = db.instance_create(self.context, values)
+ _setup_networking(instance['id'])
+ result = db.instance_get_project_vpn(self.context.elevated(),
+ self.project_id)
+ self.assertEqual(instance['id'], result['id'])
+ self.assertEqual(result['fixed_ips'][0]['floating_ips'][0].address,
+ '1.2.1.2')
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 0c198f1b5..156582970 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -32,7 +32,6 @@ from nova import flags
from nova import test
from nova import utils
from nova.api.ec2 import cloud
-from nova.auth import manager
from nova.compute import power_state
from nova.virt.libvirt import connection
from nova.virt.libvirt import firewall
@@ -54,9 +53,13 @@ def _create_network_info(count=1, ipv6=None):
fake_ip = '0.0.0.0/0'
fake_ip_2 = '0.0.0.1/0'
fake_ip_3 = '0.0.0.1/0'
+ fake_vlan = 100
+ fake_bridge_interface = 'eth0'
network = {'bridge': fake,
'cidr': fake_ip,
- 'cidr_v6': fake_ip}
+ 'cidr_v6': fake_ip,
+ 'vlan': fake_vlan,
+ 'bridge_interface': fake_bridge_interface}
mapping = {'mac': fake,
'dhcp_server': fake,
'gateway': fake,
@@ -84,7 +87,7 @@ def _setup_networking(instance_id, ip='1.2.3.4'):
'virtual_interface_id': vif_ref['id']}
db.fixed_ip_create(ctxt, fixed_ip)
db.fixed_ip_update(ctxt, ip, {'allocated': True,
- 'instance_id': instance_id})
+ 'instance_id': instance_id})
class CacheConcurrencyTestCase(test.TestCase):
@@ -150,36 +153,15 @@ class LibvirtConnTestCase(test.TestCase):
super(LibvirtConnTestCase, self).setUp()
connection._late_load_cheetah()
self.flags(fake_call=True)
- self.manager = manager.AuthManager()
-
- try:
- pjs = self.manager.get_projects()
- pjs = [p for p in pjs if p.name == 'fake']
- if 0 != len(pjs):
- self.manager.delete_project(pjs[0])
-
- users = self.manager.get_users()
- users = [u for u in users if u.name == 'fake']
- if 0 != len(users):
- self.manager.delete_user(users[0])
- except Exception, e:
- pass
-
- users = self.manager.get_users()
- self.user = self.manager.create_user('fake', 'fake', 'fake',
- admin=True)
- self.project = self.manager.create_project('fake', 'fake', 'fake')
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
self.network = utils.import_object(FLAGS.network_manager)
self.context = context.get_admin_context()
FLAGS.instances_path = ''
self.call_libvirt_dependant_setup = False
+ self.test_ip = '10.11.12.13'
- def tearDown(self):
- self.manager.delete_project(self.project)
- self.manager.delete_user(self.user)
- super(LibvirtConnTestCase, self).tearDown()
-
- test_ip = '10.11.12.13'
test_instance = {'memory_kb': '1024000',
'basepath': '/some/path',
'bridge_name': 'br100',
@@ -220,9 +202,29 @@ class LibvirtConnTestCase(test.TestCase):
def setattr(self, key, val):
self.__setattr__(key, val)
+ # A fake VIF driver
+ class FakeVIFDriver(object):
+
+ def __init__(self, **kwargs):
+ pass
+
+ def setattr(self, key, val):
+ self.__setattr__(key, val)
+
+ def plug(self, instance, network, mapping):
+ return {
+ 'id': 'fake',
+ 'bridge_name': 'fake',
+ 'mac_address': 'fake',
+ 'ip_address': 'fake',
+ 'dhcp_server': 'fake',
+ 'extra_params': 'fake',
+ }
+
# Creating mocks
fake = FakeLibvirtConnection()
fakeip = FakeIptablesFirewallDriver
+ fakevif = FakeVIFDriver()
# Customizing above fake if necessary
for key, val in kwargs.items():
fake.__setattr__(key, val)
@@ -230,6 +232,8 @@ class LibvirtConnTestCase(test.TestCase):
# Inevitable mocks for connection.LibvirtConnection
self.mox.StubOutWithMock(connection.utils, 'import_class')
connection.utils.import_class(mox.IgnoreArg()).AndReturn(fakeip)
+ self.mox.StubOutWithMock(connection.utils, 'import_object')
+ connection.utils.import_object(mox.IgnoreArg()).AndReturn(fakevif)
self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn')
connection.LibvirtConnection._conn = fake
@@ -265,7 +269,6 @@ class LibvirtConnTestCase(test.TestCase):
return db.service_create(context.get_admin_context(), service_ref)
- @test.skip_test("Please review this test to ensure intent")
def test_preparing_xml_info(self):
conn = connection.LibvirtConnection(True)
instance_ref = db.instance_create(self.context, self.test_instance)
@@ -281,43 +284,23 @@ class LibvirtConnTestCase(test.TestCase):
_create_network_info(2))
self.assertTrue(len(result['nics']) == 2)
- def test_get_nic_for_xml_v4(self):
- conn = connection.LibvirtConnection(True)
- network, mapping = _create_network_info()[0]
- self.flags(use_ipv6=False)
- params = conn._get_nic_for_xml(network, mapping)['extra_params']
- self.assertTrue(params.find('PROJNETV6') == -1)
- self.assertTrue(params.find('PROJMASKV6') == -1)
-
- def test_get_nic_for_xml_v6(self):
- conn = connection.LibvirtConnection(True)
- network, mapping = _create_network_info()[0]
- self.flags(use_ipv6=True)
- params = conn._get_nic_for_xml(network, mapping)['extra_params']
- self.assertTrue(params.find('PROJNETV6') > -1)
- self.assertTrue(params.find('PROJMASKV6') > -1)
-
- @test.skip_test("skipping libvirt tests depends on get_network_info shim")
def test_xml_and_uri_no_ramdisk_no_kernel(self):
instance_data = dict(self.test_instance)
self._check_xml_and_uri(instance_data,
expect_kernel=False, expect_ramdisk=False)
- @test.skip_test("skipping libvirt tests depends on get_network_info shim")
def test_xml_and_uri_no_ramdisk(self):
instance_data = dict(self.test_instance)
instance_data['kernel_id'] = 'aki-deadbeef'
self._check_xml_and_uri(instance_data,
expect_kernel=True, expect_ramdisk=False)
- @test.skip_test("skipping libvirt tests depends on get_network_info shim")
def test_xml_and_uri_no_kernel(self):
instance_data = dict(self.test_instance)
instance_data['ramdisk_id'] = 'ari-deadbeef'
self._check_xml_and_uri(instance_data,
expect_kernel=False, expect_ramdisk=False)
- @test.skip_test("skipping libvirt tests depends on get_network_info shim")
def test_xml_and_uri(self):
instance_data = dict(self.test_instance)
instance_data['ramdisk_id'] = 'ari-deadbeef'
@@ -325,7 +308,6 @@ class LibvirtConnTestCase(test.TestCase):
self._check_xml_and_uri(instance_data,
expect_kernel=True, expect_ramdisk=True)
- @test.skip_test("skipping libvirt tests depends on get_network_info shim")
def test_xml_and_uri_rescue(self):
instance_data = dict(self.test_instance)
instance_data['ramdisk_id'] = 'ari-deadbeef'
@@ -333,7 +315,6 @@ class LibvirtConnTestCase(test.TestCase):
self._check_xml_and_uri(instance_data, expect_kernel=True,
expect_ramdisk=True, rescue=True)
- @test.skip_test("skipping libvirt tests depends on get_network_info shim")
def test_lxc_container_and_uri(self):
instance_data = dict(self.test_instance)
self._check_xml_and_container(instance_data)
@@ -413,6 +394,15 @@ class LibvirtConnTestCase(test.TestCase):
self.assertEquals(snapshot['status'], 'active')
self.assertEquals(snapshot['name'], snapshot_name)
+ def test_attach_invalid_device(self):
+ self.create_fake_libvirt_mock()
+ connection.LibvirtConnection._conn.lookupByName = self.fake_lookup
+ self.mox.ReplayAll()
+ conn = connection.LibvirtConnection(False)
+ self.assertRaises(exception.InvalidDevicePath,
+ conn.attach_volume,
+ "fake", "bad/device/path", "/dev/fake")
+
def test_multi_nic(self):
instance_data = dict(self.test_instance)
network_info = _create_network_info(2)
@@ -430,27 +420,10 @@ class LibvirtConnTestCase(test.TestCase):
self.assertEquals(parameters[1].get('value'), 'fake')
def _check_xml_and_container(self, instance):
- user_context = context.RequestContext(project=self.project,
- user=self.user)
+ user_context = context.RequestContext(self.user_id,
+ self.project_id)
instance_ref = db.instance_create(user_context, instance)
- # Re-get the instance so it's bound to an actual session
- instance_ref = db.instance_get(user_context, instance_ref['id'])
- network_ref = db.project_get_networks(context.get_admin_context(),
- self.project.id)[0]
-
- vif = {'address': '56:12:12:12:12:12',
- 'network_id': network_ref['id'],
- 'instance_id': instance_ref['id']}
- vif_ref = db.virtual_interface_create(self.context, vif)
- fixed_ip = {'address': self.test_ip,
- 'network_id': network_ref['id'],
- 'virtual_interface_id': vif_ref['id']}
-
- ctxt = context.get_admin_context()
- fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip)
- db.fixed_ip_update(ctxt, self.test_ip,
- {'allocated': True,
- 'instance_id': instance_ref['id']})
+ _setup_networking(instance_ref['id'], self.test_ip)
self.flags(libvirt_type='lxc')
conn = connection.LibvirtConnection(True)
@@ -476,13 +449,12 @@ class LibvirtConnTestCase(test.TestCase):
def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel,
rescue=False):
- user_context = context.RequestContext(project=self.project,
- user=self.user)
+ user_context = context.RequestContext(self.user_id, self.project_id)
instance_ref = db.instance_create(user_context, instance)
network_ref = db.project_get_networks(context.get_admin_context(),
- self.project.id)[0]
+ self.project_id)[0]
- _setup_networking(instance_ref['id'], ip=self.test_ip)
+ _setup_networking(instance_ref['id'], self.test_ip)
type_uri_map = {'qemu': ('qemu:///system',
[(lambda t: t.find('.').get('type'), 'qemu'),
@@ -723,6 +695,9 @@ class LibvirtConnTestCase(test.TestCase):
return vdmock
self.create_fake_libvirt_mock(lookupByName=fake_lookup)
+ self.mox.StubOutWithMock(self.compute, "recover_live_migration")
+ self.compute.recover_live_migration(self.context, instance_ref,
+ dest='dest')
# Start test
self.mox.ReplayAll()
@@ -741,7 +716,6 @@ class LibvirtConnTestCase(test.TestCase):
db.volume_destroy(self.context, volume_ref['id'])
db.instance_destroy(self.context, instance_ref['id'])
- @test.skip_test("test needs rewrite: instance no longer has mac_address")
def test_spawn_with_network_info(self):
# Skip if non-libvirt environment
if not self.lazy_load_library_exists():
@@ -760,17 +734,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.firewall_driver.setattr('setup_basic_filtering', fake_none)
conn.firewall_driver.setattr('prepare_instance_filter', fake_none)
- network = db.project_get_networks(context.get_admin_context(),
- self.project.id)[0]
- ip_dict = {'ip': self.test_ip,
- 'netmask': network['netmask'],
- 'enabled': '1'}
- mapping = {'label': network['label'],
- 'gateway': network['gateway'],
- 'mac': instance['mac_address'],
- 'dns': [network['dns']],
- 'ips': [ip_dict]}
- network_info = [(network, mapping)]
+ network_info = _create_network_info()
try:
conn.spawn(instance, network_info)
@@ -852,11 +816,9 @@ class IptablesFirewallTestCase(test.TestCase):
def setUp(self):
super(IptablesFirewallTestCase, self).setUp()
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('fake', 'fake', 'fake',
- admin=True)
- self.project = self.manager.create_project('fake', 'fake', 'fake')
- self.context = context.RequestContext('fake', 'fake')
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
self.network = utils.import_object(FLAGS.network_manager)
class FakeLibvirtConnection(object):
@@ -864,6 +826,7 @@ class IptablesFirewallTestCase(test.TestCase):
"""setup_basic_rules in nwfilter calls this."""
pass
self.fake_libvirt_connection = FakeLibvirtConnection()
+ self.test_ip = '10.11.12.13'
self.fw = firewall.IptablesFirewallDriver(
get_connection=lambda: self.fake_libvirt_connection)
@@ -881,11 +844,6 @@ class IptablesFirewallTestCase(test.TestCase):
connection.libxml2 = __import__('libxml2')
return True
- def tearDown(self):
- self.manager.delete_project(self.project)
- self.manager.delete_user(self.user)
- super(IptablesFirewallTestCase, self).tearDown()
-
in_nat_rules = [
'# Generated by iptables-save v1.4.10 on Sat Feb 19 00:03:19 2011',
'*nat',
@@ -929,27 +887,11 @@ class IptablesFirewallTestCase(test.TestCase):
'project_id': 'fake',
'instance_type_id': 1})
- @test.skip_test("skipping libvirt tests depends on get_network_info shim")
def test_static_filters(self):
instance_ref = self._create_instance_ref()
- ip = '10.11.12.13'
-
- network_ref = db.project_get_networks(self.context,
- 'fake',
- associate=True)[0]
- vif = {'address': '56:12:12:12:12:12',
- 'network_id': network_ref['id'],
- 'instance_id': instance_ref['id']}
- vif_ref = db.virtual_interface_create(self.context, vif)
-
- fixed_ip = {'address': ip,
- 'network_id': network_ref['id'],
- 'virtual_interface_id': vif_ref['id']}
- admin_ctxt = context.get_admin_context()
- db.fixed_ip_create(admin_ctxt, fixed_ip)
- db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
- 'instance_id': instance_ref['id']})
+ _setup_networking(instance_ref['id'], self.test_ip)
+ admin_ctxt = context.get_admin_context()
secgroup = db.security_group_create(admin_ctxt,
{'user_id': 'fake',
'project_id': 'fake',
@@ -1081,7 +1023,6 @@ class IptablesFirewallTestCase(test.TestCase):
self.assertEquals(ipv6_network_rules,
ipv6_rules_per_network * networks_count)
- @test.skip_test("skipping libvirt tests")
def test_do_refresh_security_group_rules(self):
instance_ref = self._create_instance_ref()
self.mox.StubOutWithMock(self.fw,
@@ -1092,7 +1033,6 @@ class IptablesFirewallTestCase(test.TestCase):
self.mox.ReplayAll()
self.fw.do_refresh_security_group_rules("fake")
- @test.skip_test("skip libvirt test project_get_network no longer exists")
def test_unfilter_instance_undefines_nwfilter(self):
# Skip if non-libvirt environment
if not self.lazy_load_library_exists():
@@ -1106,38 +1046,24 @@ class IptablesFirewallTestCase(test.TestCase):
self.fw.nwfilter._conn.nwfilterLookupByName =\
fakefilter.nwfilterLookupByName
instance_ref = self._create_instance_ref()
- inst_id = instance_ref['id']
- instance = db.instance_get(self.context, inst_id)
- ip = '10.11.12.13'
- network_ref = db.project_get_network(self.context, 'fake')
- fixed_ip = {'address': ip, 'network_id': network_ref['id']}
- db.fixed_ip_create(admin_ctxt, fixed_ip)
- db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
- 'instance_id': inst_id})
- self.fw.setup_basic_filtering(instance)
- self.fw.prepare_instance_filter(instance)
- self.fw.apply_instance_filter(instance)
+ _setup_networking(instance_ref['id'], self.test_ip)
+ self.fw.setup_basic_filtering(instance_ref)
+ self.fw.prepare_instance_filter(instance_ref)
+ self.fw.apply_instance_filter(instance_ref)
original_filter_count = len(fakefilter.filters)
- self.fw.unfilter_instance(instance)
+ self.fw.unfilter_instance(instance_ref)
# should undefine just the instance filter
self.assertEqual(original_filter_count - len(fakefilter.filters), 1)
db.instance_destroy(admin_ctxt, instance_ref['id'])
- @test.skip_test("skip libvirt test project_get_network no longer exists")
def test_provider_firewall_rules(self):
# setup basic instance data
instance_ref = self._create_instance_ref()
nw_info = _create_network_info(1)
- ip = '10.11.12.13'
- network_ref = db.project_get_network(self.context, 'fake')
- admin_ctxt = context.get_admin_context()
- fixed_ip = {'address': ip, 'network_id': network_ref['id']}
- db.fixed_ip_create(admin_ctxt, fixed_ip)
- db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
- 'instance_id': instance_ref['id']})
+ _setup_networking(instance_ref['id'], self.test_ip)
# FRAGILE: peeks at how the firewall names chains
chain_name = 'inst-%s' % instance_ref['id']
@@ -1149,6 +1075,7 @@ class IptablesFirewallTestCase(test.TestCase):
if rule.chain == 'provider']
self.assertEqual(0, len(rules))
+ admin_ctxt = context.get_admin_context()
# add a rule and send the update message, check for 1 rule
provider_fw0 = db.provider_fw_rule_create(admin_ctxt,
{'protocol': 'tcp',
@@ -1199,22 +1126,16 @@ class NWFilterTestCase(test.TestCase):
class Mock(object):
pass
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('fake', 'fake', 'fake',
- admin=True)
- self.project = self.manager.create_project('fake', 'fake', 'fake')
- self.context = context.RequestContext(self.user, self.project)
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
self.fake_libvirt_connection = Mock()
+ self.test_ip = '10.11.12.13'
self.fw = firewall.NWFilterFirewall(
lambda: self.fake_libvirt_connection)
- def tearDown(self):
- self.manager.delete_project(self.project)
- self.manager.delete_user(self.user)
- super(NWFilterTestCase, self).tearDown()
-
def test_cidr_rule_nwfilter_xml(self):
cloud_controller = cloud.CloudController()
cloud_controller.create_security_group(self.context,
@@ -1293,7 +1214,6 @@ class NWFilterTestCase(test.TestCase):
inst.update(params)
return db.instance_type_create(context, inst)['id']
- @test.skip_test('Skipping this test')
def test_creates_base_rule_first(self):
# These come pre-defined by libvirt
self.defined_filters = ['no-mac-spoofing',
@@ -1325,21 +1245,11 @@ class NWFilterTestCase(test.TestCase):
instance_ref = self._create_instance()
inst_id = instance_ref['id']
- ip = '10.11.12.13'
-
- #network_ref = db.project_get_networks(self.context, 'fake')[0]
- #fixed_ip = {'address': ip, 'network_id': network_ref['id']}
-
- #admin_ctxt = context.get_admin_context()
- #db.fixed_ip_create(admin_ctxt, fixed_ip)
- #db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
- # 'instance_id': inst_id})
-
- self._setup_networking(instance_ref['id'], ip=ip)
+ _setup_networking(instance_ref['id'], self.test_ip)
def _ensure_all_called():
instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'],
- '00A0C914C829')
+ '561212121212')
secgroup_filter = 'nova-secgroup-%s' % self.security_group['id']
for required in [secgroup_filter, 'allow-dhcp-server',
'no-arp-spoofing', 'no-ip-spoofing',
@@ -1360,7 +1270,7 @@ class NWFilterTestCase(test.TestCase):
self.fw.apply_instance_filter(instance)
_ensure_all_called()
self.teardown_security_group()
- db.instance_destroy(admin_ctxt, instance_ref['id'])
+ db.instance_destroy(context.get_admin_context(), instance_ref['id'])
def test_create_network_filters(self):
instance_ref = self._create_instance()
@@ -1370,7 +1280,6 @@ class NWFilterTestCase(test.TestCase):
"fake")
self.assertEquals(len(result), 3)
- @test.skip_test("skip libvirt test project_get_network no longer exists")
def test_unfilter_instance_undefines_nwfilters(self):
admin_ctxt = context.get_admin_context()
@@ -1388,12 +1297,7 @@ class NWFilterTestCase(test.TestCase):
instance = db.instance_get(self.context, inst_id)
- ip = '10.11.12.13'
- network_ref = db.project_get_network(self.context, 'fake')
- fixed_ip = {'address': ip, 'network_id': network_ref['id']}
- db.fixed_ip_create(admin_ctxt, fixed_ip)
- db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
- 'instance_id': inst_id})
+ _setup_networking(instance_ref['id'], self.test_ip)
self.fw.setup_basic_filtering(instance)
self.fw.prepare_instance_filter(instance)
self.fw.apply_instance_filter(instance)
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index fbe7d769e..28f50d328 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -149,7 +149,10 @@ class FlatNetworkTestCase(test.TestCase):
'cidr': '192.168.%s.0/24' % i,
'cidr_v6': '2001:db%s::/64' % i8,
'id': i,
- 'injected': 'DONTCARE'}
+ 'multi_host': False,
+ 'injected': 'DONTCARE',
+ 'bridge_interface': 'fake_fa%s' % i,
+ 'vlan': None}
self.assertDictMatch(nw[0], check)
@@ -162,7 +165,9 @@ class FlatNetworkTestCase(test.TestCase):
'ips': 'DONTCARE',
'label': 'test%s' % i,
'mac': 'DE:AD:BE:EF:00:0%s' % i,
- 'rxtx_cap': 'DONTCARE'}
+ 'rxtx_cap': 'DONTCARE',
+ 'should_create_vlan': False,
+ 'should_create_bridge': False}
self.assertDictMatch(nw[1], check)
check = [{'enabled': 'DONTCARE',
diff --git a/nova/tests/test_objectstore.py b/nova/tests/test_objectstore.py
index 39b4e18d7..0b2dce20e 100644
--- a/nova/tests/test_objectstore.py
+++ b/nova/tests/test_objectstore.py
@@ -21,8 +21,6 @@ Unittets for S3 objectstore clone.
"""
import boto
-import glob
-import hashlib
import os
import shutil
import tempfile
@@ -30,12 +28,9 @@ import tempfile
from boto import exception as boto_exception
from boto.s3 import connection as s3
-from nova import context
-from nova import exception
from nova import flags
from nova import wsgi
from nova import test
-from nova.auth import manager
from nova.objectstore import s3server
@@ -57,15 +52,9 @@ class S3APITestCase(test.TestCase):
def setUp(self):
"""Setup users, projects, and start a test server."""
super(S3APITestCase, self).setUp()
- self.flags(auth_driver='nova.auth.ldapdriver.FakeLdapDriver',
- buckets_path=os.path.join(OSS_TEMPDIR, 'buckets'),
+ self.flags(buckets_path=os.path.join(OSS_TEMPDIR, 'buckets'),
s3_host='127.0.0.1')
- self.auth_manager = manager.AuthManager()
- self.admin_user = self.auth_manager.create_user('admin', admin=True)
- self.admin_project = self.auth_manager.create_project('admin',
- self.admin_user)
-
shutil.rmtree(FLAGS.buckets_path)
os.mkdir(FLAGS.buckets_path)
@@ -80,8 +69,8 @@ class S3APITestCase(test.TestCase):
boto.config.add_section('Boto')
boto.config.set('Boto', 'num_retries', '0')
- conn = s3.S3Connection(aws_access_key_id=self.admin_user.access,
- aws_secret_access_key=self.admin_user.secret,
+ conn = s3.S3Connection(aws_access_key_id='fake',
+ aws_secret_access_key='fake',
host=FLAGS.s3_host,
port=FLAGS.s3_port,
is_secure=False,
@@ -104,11 +93,11 @@ class S3APITestCase(test.TestCase):
self.assertEquals(buckets[0].name, name, "Wrong name")
return True
- def test_000_list_buckets(self):
+ def test_list_buckets(self):
"""Make sure we are starting with no buckets."""
self._ensure_no_buckets(self.conn.get_all_buckets())
- def test_001_create_and_delete_bucket(self):
+ def test_create_and_delete_bucket(self):
"""Test bucket creation and deletion."""
bucket_name = 'testbucket'
@@ -117,7 +106,7 @@ class S3APITestCase(test.TestCase):
self.conn.delete_bucket(bucket_name)
self._ensure_no_buckets(self.conn.get_all_buckets())
- def test_002_create_bucket_and_key_and_delete_key_again(self):
+ def test_create_bucket_and_key_and_delete_key_again(self):
"""Test key operations on buckets."""
bucket_name = 'testbucket'
key_name = 'somekey'
@@ -146,8 +135,6 @@ class S3APITestCase(test.TestCase):
bucket_name)
def tearDown(self):
- """Tear down auth and test server."""
- self.auth_manager.delete_user('admin')
- self.auth_manager.delete_project('admin')
+ """Tear down test server."""
self.server.stop()
super(S3APITestCase, self).tearDown()
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index 69d2deafe..92393b536 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -20,12 +20,9 @@ from nova import compute
from nova import context
from nova import db
from nova import flags
-from nova import network
from nova import quota
from nova import test
-from nova import utils
from nova import volume
-from nova.auth import manager
from nova.compute import instance_types
@@ -48,25 +45,20 @@ class QuotaTestCase(test.TestCase):
quota_gigabytes=20,
quota_floating_ips=1)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('admin', 'admin', 'admin', True)
- self.project = self.manager.create_project('admin', 'admin', 'admin')
self.network = self.network = self.start_service('network')
- self.context = context.RequestContext(project=self.project,
- user=self.user)
-
- def tearDown(self):
- manager.AuthManager().delete_project(self.project)
- manager.AuthManager().delete_user(self.user)
- super(QuotaTestCase, self).tearDown()
+ self.user_id = 'admin'
+ self.project_id = 'admin'
+ self.context = context.RequestContext(self.user_id,
+ self.project_id,
+ True)
def _create_instance(self, cores=2):
"""Create a test instance"""
inst = {}
inst['image_id'] = 1
inst['reservation_id'] = 'r-fakeres'
- inst['user_id'] = self.user.id
- inst['project_id'] = self.project.id
+ inst['user_id'] = self.user_id
+ inst['project_id'] = self.project_id
inst['instance_type_id'] = '3' # m1.large
inst['vcpus'] = cores
return db.instance_create(self.context, inst)['id']
@@ -74,8 +66,8 @@ class QuotaTestCase(test.TestCase):
def _create_volume(self, size=10):
"""Create a test volume"""
vol = {}
- vol['user_id'] = self.user.id
- vol['project_id'] = self.project.id
+ vol['user_id'] = self.user_id
+ vol['project_id'] = self.project_id
vol['size'] = size
return db.volume_create(self.context, vol)['id']
@@ -95,15 +87,15 @@ class QuotaTestCase(test.TestCase):
num_instances = quota.allowed_instances(self.context, 100,
self._get_instance_type('m1.small'))
self.assertEqual(num_instances, 2)
- db.quota_create(self.context, self.project.id, 'instances', 10)
+ db.quota_create(self.context, self.project_id, 'instances', 10)
num_instances = quota.allowed_instances(self.context, 100,
self._get_instance_type('m1.small'))
self.assertEqual(num_instances, 4)
- db.quota_create(self.context, self.project.id, 'cores', 100)
+ db.quota_create(self.context, self.project_id, 'cores', 100)
num_instances = quota.allowed_instances(self.context, 100,
self._get_instance_type('m1.small'))
self.assertEqual(num_instances, 10)
- db.quota_create(self.context, self.project.id, 'ram', 3 * 2048)
+ db.quota_create(self.context, self.project_id, 'ram', 3 * 2048)
num_instances = quota.allowed_instances(self.context, 100,
self._get_instance_type('m1.small'))
self.assertEqual(num_instances, 3)
@@ -113,13 +105,13 @@ class QuotaTestCase(test.TestCase):
num_metadata_items = quota.allowed_metadata_items(self.context,
too_many_items)
self.assertEqual(num_metadata_items, FLAGS.quota_metadata_items)
- db.quota_create(self.context, self.project.id, 'metadata_items', 5)
+ db.quota_create(self.context, self.project_id, 'metadata_items', 5)
num_metadata_items = quota.allowed_metadata_items(self.context,
too_many_items)
self.assertEqual(num_metadata_items, 5)
# Cleanup
- db.quota_destroy_all_by_project(self.context, self.project.id)
+ db.quota_destroy_all_by_project(self.context, self.project_id)
def test_unlimited_instances(self):
FLAGS.quota_instances = 2
@@ -129,7 +121,7 @@ class QuotaTestCase(test.TestCase):
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 2)
- db.quota_create(self.context, self.project.id, 'instances', None)
+ db.quota_create(self.context, self.project_id, 'instances', None)
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 100)
@@ -145,7 +137,7 @@ class QuotaTestCase(test.TestCase):
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 2)
- db.quota_create(self.context, self.project.id, 'ram', None)
+ db.quota_create(self.context, self.project_id, 'ram', None)
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 100)
@@ -161,7 +153,7 @@ class QuotaTestCase(test.TestCase):
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 2)
- db.quota_create(self.context, self.project.id, 'cores', None)
+ db.quota_create(self.context, self.project_id, 'cores', None)
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
self.assertEqual(num_instances, 100)
@@ -174,7 +166,7 @@ class QuotaTestCase(test.TestCase):
FLAGS.quota_gigabytes = -1
volumes = quota.allowed_volumes(self.context, 100, 1)
self.assertEqual(volumes, 10)
- db.quota_create(self.context, self.project.id, 'volumes', None)
+ db.quota_create(self.context, self.project_id, 'volumes', None)
volumes = quota.allowed_volumes(self.context, 100, 1)
self.assertEqual(volumes, 100)
volumes = quota.allowed_volumes(self.context, 101, 1)
@@ -185,7 +177,7 @@ class QuotaTestCase(test.TestCase):
FLAGS.quota_gigabytes = 10
volumes = quota.allowed_volumes(self.context, 100, 1)
self.assertEqual(volumes, 10)
- db.quota_create(self.context, self.project.id, 'gigabytes', None)
+ db.quota_create(self.context, self.project_id, 'gigabytes', None)
volumes = quota.allowed_volumes(self.context, 100, 1)
self.assertEqual(volumes, 100)
volumes = quota.allowed_volumes(self.context, 101, 1)
@@ -195,7 +187,7 @@ class QuotaTestCase(test.TestCase):
FLAGS.quota_floating_ips = 10
floating_ips = quota.allowed_floating_ips(self.context, 100)
self.assertEqual(floating_ips, 10)
- db.quota_create(self.context, self.project.id, 'floating_ips', None)
+ db.quota_create(self.context, self.project_id, 'floating_ips', None)
floating_ips = quota.allowed_floating_ips(self.context, 100)
self.assertEqual(floating_ips, 100)
floating_ips = quota.allowed_floating_ips(self.context, 101)
@@ -205,7 +197,7 @@ class QuotaTestCase(test.TestCase):
FLAGS.quota_metadata_items = 10
items = quota.allowed_metadata_items(self.context, 100)
self.assertEqual(items, 10)
- db.quota_create(self.context, self.project.id, 'metadata_items', None)
+ db.quota_create(self.context, self.project_id, 'metadata_items', None)
items = quota.allowed_metadata_items(self.context, 100)
self.assertEqual(items, 100)
items = quota.allowed_metadata_items(self.context, 101)
@@ -269,16 +261,15 @@ class QuotaTestCase(test.TestCase):
for volume_id in volume_ids:
db.volume_destroy(self.context, volume_id)
- @test.skip_test
def test_too_many_addresses(self):
address = '192.168.0.100'
db.floating_ip_create(context.get_admin_context(),
- {'address': address, 'host': FLAGS.host,
- 'project_id': self.project.id})
+ {'address': address,
+ 'project_id': self.project_id})
self.assertRaises(quota.QuotaError,
self.network.allocate_floating_ip,
self.context,
- self.project.id)
+ self.project_id)
db.floating_ip_destroy(context.get_admin_context(), address)
def test_too_many_metadata_items(self):
@@ -300,7 +291,7 @@ class QuotaTestCase(test.TestCase):
def test_overridden_allowed_injected_files(self):
FLAGS.quota_max_injected_files = 5
- db.quota_create(self.context, self.project.id, 'injected_files', 77)
+ db.quota_create(self.context, self.project_id, 'injected_files', 77)
self.assertEqual(quota.allowed_injected_files(self.context, 100), 77)
def test_unlimited_default_allowed_injected_files(self):
@@ -309,7 +300,7 @@ class QuotaTestCase(test.TestCase):
def test_unlimited_db_allowed_injected_files(self):
FLAGS.quota_max_injected_files = 5
- db.quota_create(self.context, self.project.id, 'injected_files', None)
+ db.quota_create(self.context, self.project_id, 'injected_files', None)
self.assertEqual(quota.allowed_injected_files(self.context, 100), 100)
def test_default_allowed_injected_file_content_bytes(self):
@@ -319,7 +310,7 @@ class QuotaTestCase(test.TestCase):
def test_overridden_allowed_injected_file_content_bytes(self):
FLAGS.quota_max_injected_file_content_bytes = 12345
- db.quota_create(self.context, self.project.id,
+ db.quota_create(self.context, self.project_id,
'injected_file_content_bytes', 5678)
limit = quota.allowed_injected_file_content_bytes(self.context, 23456)
self.assertEqual(limit, 5678)
@@ -331,7 +322,7 @@ class QuotaTestCase(test.TestCase):
def test_unlimited_db_allowed_injected_file_content_bytes(self):
FLAGS.quota_max_injected_file_content_bytes = 12345
- db.quota_create(self.context, self.project.id,
+ db.quota_create(self.context, self.project_id,
'injected_file_content_bytes', None)
limit = quota.allowed_injected_file_content_bytes(self.context, 23456)
self.assertEqual(limit, 23456)
diff --git a/nova/tests/test_rpc.py b/nova/tests/test_rpc.py
index ffd748efe..2d2436175 100644
--- a/nova/tests/test_rpc.py
+++ b/nova/tests/test_rpc.py
@@ -33,11 +33,12 @@ LOG = logging.getLogger('nova.tests.rpc')
class RpcTestCase(test.TestCase):
def setUp(self):
super(RpcTestCase, self).setUp()
- self.conn = rpc.Connection.instance(True)
+ self.conn = rpc.create_connection(True)
self.receiver = TestReceiver()
- self.consumer = rpc.TopicAdapterConsumer(connection=self.conn,
- topic='test',
- proxy=self.receiver)
+ self.consumer = rpc.create_consumer(self.conn,
+ 'test',
+ self.receiver,
+ False)
self.consumer.attach_to_eventlet()
self.context = context.get_admin_context()
@@ -129,6 +130,8 @@ class RpcTestCase(test.TestCase):
"""Calls echo in the passed queue"""
LOG.debug(_("Nested received %(queue)s, %(value)s")
% locals())
+ # TODO: so, it will replay the context and use the same REQID?
+ # that's bizarre.
ret = rpc.call(context,
queue,
{"method": "echo",
@@ -137,10 +140,11 @@ class RpcTestCase(test.TestCase):
return value
nested = Nested()
- conn = rpc.Connection.instance(True)
- consumer = rpc.TopicAdapterConsumer(connection=conn,
- topic='nested',
- proxy=nested)
+ conn = rpc.create_connection(True)
+ consumer = rpc.create_consumer(conn,
+ 'nested',
+ nested,
+ False)
consumer.attach_to_eventlet()
value = 42
result = rpc.call(self.context,
@@ -149,47 +153,6 @@ class RpcTestCase(test.TestCase):
"value": value}})
self.assertEqual(value, result)
- def test_connectionpool_single(self):
- """Test that ConnectionPool recycles a single connection."""
- conn1 = rpc.ConnectionPool.get()
- rpc.ConnectionPool.put(conn1)
- conn2 = rpc.ConnectionPool.get()
- rpc.ConnectionPool.put(conn2)
- self.assertEqual(conn1, conn2)
-
- def test_connectionpool_double(self):
- """Test that ConnectionPool returns and reuses separate connections.
-
- When called consecutively we should get separate connections and upon
- returning them those connections should be reused for future calls
- before generating a new connection.
-
- """
- conn1 = rpc.ConnectionPool.get()
- conn2 = rpc.ConnectionPool.get()
-
- self.assertNotEqual(conn1, conn2)
- rpc.ConnectionPool.put(conn1)
- rpc.ConnectionPool.put(conn2)
-
- conn3 = rpc.ConnectionPool.get()
- conn4 = rpc.ConnectionPool.get()
- self.assertEqual(conn1, conn3)
- self.assertEqual(conn2, conn4)
-
- def test_connectionpool_limit(self):
- """Test connection pool limit and connection uniqueness."""
- max_size = FLAGS.rpc_conn_pool_size
- conns = []
-
- for i in xrange(max_size):
- conns.append(rpc.ConnectionPool.get())
-
- self.assertFalse(rpc.ConnectionPool.free_items)
- self.assertEqual(rpc.ConnectionPool.current_size,
- rpc.ConnectionPool.max_size)
- self.assertEqual(len(set(conns)), max_size)
-
class TestReceiver(object):
"""Simple Proxy class so the consumer has methods to call.
diff --git a/nova/tests/test_rpc_amqp.py b/nova/tests/test_rpc_amqp.py
new file mode 100644
index 000000000..d29f7ae32
--- /dev/null
+++ b/nova/tests/test_rpc_amqp.py
@@ -0,0 +1,68 @@
+from nova import context
+from nova import flags
+from nova import log as logging
+from nova import rpc
+from nova.rpc import amqp
+from nova import test
+
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.rpc')
+
+
+class RpcAMQPTestCase(test.TestCase):
+ def setUp(self):
+ super(RpcAMQPTestCase, self).setUp()
+ self.conn = rpc.create_connection(True)
+ self.receiver = TestReceiver()
+ self.consumer = rpc.create_consumer(self.conn,
+ 'test',
+ self.receiver,
+ False)
+ self.consumer.attach_to_eventlet()
+ self.context = context.get_admin_context()
+
+ def test_connectionpool_single(self):
+ """Test that ConnectionPool recycles a single connection."""
+ conn1 = amqp.ConnectionPool.get()
+ amqp.ConnectionPool.put(conn1)
+ conn2 = amqp.ConnectionPool.get()
+ amqp.ConnectionPool.put(conn2)
+ self.assertEqual(conn1, conn2)
+
+
+class TestReceiver(object):
+ """Simple Proxy class so the consumer has methods to call.
+
+ Uses static methods because we aren't actually storing any state.
+
+ """
+
+ @staticmethod
+ def echo(context, value):
+ """Simply returns whatever value is sent in."""
+ LOG.debug(_("Received %s"), value)
+ return value
+
+ @staticmethod
+ def context(context, value):
+ """Returns dictionary version of context."""
+ LOG.debug(_("Received %s"), context)
+ return context.to_dict()
+
+ @staticmethod
+ def echo_three_times(context, value):
+ context.reply(value)
+ context.reply(value + 1)
+ context.reply(value + 2)
+
+ @staticmethod
+ def echo_three_times_yield(context, value):
+ yield value
+ yield value + 1
+ yield value + 2
+
+ @staticmethod
+ def fail(context, value):
+ """Raises an exception with the value sent in."""
+ raise Exception(value)
diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py
index f45f76b73..bbf47b50f 100644
--- a/nova/tests/test_service.py
+++ b/nova/tests/test_service.py
@@ -109,103 +109,8 @@ class ServiceTestCase(test.TestCase):
# the looping calls are created in StartService.
app = service.Service.create(host=host, binary=binary, topic=topic)
- self.mox.StubOutWithMock(service.rpc.Connection, 'instance')
- service.rpc.Connection.instance(new=mox.IgnoreArg())
-
- self.mox.StubOutWithMock(rpc,
- 'TopicAdapterConsumer',
- use_mock_anything=True)
- self.mox.StubOutWithMock(rpc,
- 'FanoutAdapterConsumer',
- use_mock_anything=True)
-
- self.mox.StubOutWithMock(rpc,
- 'ConsumerSet',
- use_mock_anything=True)
-
- rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
- topic=topic,
- proxy=mox.IsA(service.Service)).AndReturn(
- rpc.TopicAdapterConsumer)
-
- rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
- topic='%s.%s' % (topic, host),
- proxy=mox.IsA(service.Service)).AndReturn(
- rpc.TopicAdapterConsumer)
-
- rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(),
- topic=topic,
- proxy=mox.IsA(service.Service)).AndReturn(
- rpc.FanoutAdapterConsumer)
-
- def wait_func(self, limit=None):
- return None
-
- mock_cset = self.mox.CreateMock(rpc.ConsumerSet,
- {'wait': wait_func})
- rpc.ConsumerSet(connection=mox.IgnoreArg(),
- consumer_list=mox.IsA(list)).AndReturn(mock_cset)
- wait_func(mox.IgnoreArg())
-
- service_create = {'host': host,
- 'binary': binary,
- 'topic': topic,
- 'report_count': 0,
- 'availability_zone': 'nova'}
- service_ref = {'host': host,
- 'binary': binary,
- 'report_count': 0,
- 'id': 1}
-
- service.db.service_get_by_args(mox.IgnoreArg(),
- host,
- binary).AndRaise(exception.NotFound())
- service.db.service_create(mox.IgnoreArg(),
- service_create).AndReturn(service_ref)
- self.mox.ReplayAll()
-
- app.start()
- app.stop()
self.assert_(app)
- # We're testing sort of weird behavior in how report_state decides
- # whether it is disconnected, it looks for a variable on itself called
- # 'model_disconnected' and report_state doesn't really do much so this
- # these are mostly just for coverage
- def test_report_state_no_service(self):
- host = 'foo'
- binary = 'bar'
- topic = 'test'
- service_create = {'host': host,
- 'binary': binary,
- 'topic': topic,
- 'report_count': 0,
- 'availability_zone': 'nova'}
- service_ref = {'host': host,
- 'binary': binary,
- 'topic': topic,
- 'report_count': 0,
- 'availability_zone': 'nova',
- 'id': 1}
-
- service.db.service_get_by_args(mox.IgnoreArg(),
- host,
- binary).AndRaise(exception.NotFound())
- service.db.service_create(mox.IgnoreArg(),
- service_create).AndReturn(service_ref)
- service.db.service_get(mox.IgnoreArg(),
- service_ref['id']).AndReturn(service_ref)
- service.db.service_update(mox.IgnoreArg(), service_ref['id'],
- mox.ContainsKeyValue('report_count', 1))
-
- self.mox.ReplayAll()
- serv = service.Service(host,
- binary,
- topic,
- 'nova.tests.test_service.FakeManager')
- serv.start()
- serv.report_state()
-
def test_report_state_newly_disconnected(self):
host = 'foo'
binary = 'bar'
@@ -276,81 +181,6 @@ class ServiceTestCase(test.TestCase):
self.assert_(not serv.model_disconnected)
- def test_compute_can_update_available_resource(self):
- """Confirm compute updates their record of compute-service table."""
- host = 'foo'
- binary = 'nova-compute'
- topic = 'compute'
-
- # Any mocks are not working without UnsetStubs() here.
- self.mox.UnsetStubs()
- ctxt = context.get_admin_context()
- service_ref = db.service_create(ctxt, {'host': host,
- 'binary': binary,
- 'topic': topic})
- serv = service.Service(host,
- binary,
- topic,
- 'nova.compute.manager.ComputeManager')
-
- # This testcase want to test calling update_available_resource.
- # No need to call periodic call, then below variable must be set 0.
- serv.report_interval = 0
- serv.periodic_interval = 0
-
- # Creating mocks
- self.mox.StubOutWithMock(service.rpc.Connection, 'instance')
- service.rpc.Connection.instance(new=mox.IgnoreArg())
-
- self.mox.StubOutWithMock(rpc,
- 'TopicAdapterConsumer',
- use_mock_anything=True)
- self.mox.StubOutWithMock(rpc,
- 'FanoutAdapterConsumer',
- use_mock_anything=True)
-
- self.mox.StubOutWithMock(rpc,
- 'ConsumerSet',
- use_mock_anything=True)
-
- rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
- topic=topic,
- proxy=mox.IsA(service.Service)).AndReturn(
- rpc.TopicAdapterConsumer)
-
- rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
- topic='%s.%s' % (topic, host),
- proxy=mox.IsA(service.Service)).AndReturn(
- rpc.TopicAdapterConsumer)
-
- rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(),
- topic=topic,
- proxy=mox.IsA(service.Service)).AndReturn(
- rpc.FanoutAdapterConsumer)
-
- def wait_func(self, limit=None):
- return None
-
- mock_cset = self.mox.CreateMock(rpc.ConsumerSet,
- {'wait': wait_func})
- rpc.ConsumerSet(connection=mox.IgnoreArg(),
- consumer_list=mox.IsA(list)).AndReturn(mock_cset)
- wait_func(mox.IgnoreArg())
-
- self.mox.StubOutWithMock(serv.manager.driver,
- 'update_available_resource')
- serv.manager.driver.update_available_resource(mox.IgnoreArg(), host)
-
- # Just doing start()-stop(), not confirm new db record is created,
- # because update_available_resource() works only in
- # libvirt environment. This testcase confirms
- # update_available_resource() is called. Otherwise, mox complains.
- self.mox.ReplayAll()
- serv.start()
- serv.stop()
-
- db.service_destroy(ctxt, service_ref['id'])
-
class TestWSGIService(test.TestCase):
diff --git a/nova/tests/test_test.py b/nova/tests/test_test.py
index 35c838065..64f11fa45 100644
--- a/nova/tests/test_test.py
+++ b/nova/tests/test_test.py
@@ -33,8 +33,13 @@ class IsolationTestCase(test.TestCase):
self.start_service('compute')
def test_rpc_consumer_isolation(self):
- connection = rpc.Connection.instance(new=True)
- consumer = rpc.TopicAdapterConsumer(connection, topic='compute')
- consumer.register_callback(
- lambda x, y: self.fail('I should never be called'))
+ class NeverCalled(object):
+
+ def __getattribute__(*args):
+ assert False, "I should never get called."
+
+ connection = rpc.create_connection(new=True)
+ proxy = NeverCalled()
+ consumer = rpc.create_consumer(connection, 'compute',
+ proxy, fanout=False)
consumer.attach_to_eventlet()
diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py
index cbf7801cf..3d87d67ad 100644
--- a/nova/tests/test_vmwareapi.py
+++ b/nova/tests/test_vmwareapi.py
@@ -19,14 +19,11 @@
Test suite for VMWareAPI.
"""
-import stubout
-
from nova import context
from nova import db
from nova import flags
from nova import test
from nova import utils
-from nova.auth import manager
from nova.compute import power_state
from nova.tests.glance import stubs as glance_stubs
from nova.tests.vmwareapi import db_fakes
@@ -41,51 +38,63 @@ FLAGS = flags.FLAGS
class VMWareAPIVMTestCase(test.TestCase):
"""Unit tests for Vmware API connection calls."""
- # NOTE(jkoelker): This is leaking stubs into the db module.
- # Commenting out until updated for multi-nic.
- #def setUp(self):
- # super(VMWareAPIVMTestCase, self).setUp()
- # self.flags(vmwareapi_host_ip='test_url',
- # vmwareapi_host_username='test_username',
- # vmwareapi_host_password='test_pass')
- # self.manager = manager.AuthManager()
- # self.user = self.manager.create_user('fake', 'fake', 'fake',
- # admin=True)
- # self.project = self.manager.create_project('fake', 'fake', 'fake')
- # self.network = utils.import_object(FLAGS.network_manager)
- # self.stubs = stubout.StubOutForTesting()
- # vmwareapi_fake.reset()
- # db_fakes.stub_out_db_instance_api(self.stubs)
- # stubs.set_stubs(self.stubs)
- # glance_stubs.stubout_glance_client(self.stubs,
- # glance_stubs.FakeGlance)
- # self.conn = vmwareapi_conn.get_connection(False)
-
- #def tearDown(self):
- # super(VMWareAPIVMTestCase, self).tearDown()
- # vmwareapi_fake.cleanup()
- # self.manager.delete_project(self.project)
- # self.manager.delete_user(self.user)
- # self.stubs.UnsetAll()
+ def setUp(self):
+ super(VMWareAPIVMTestCase, self).setUp()
+ self.flags(vmwareapi_host_ip='test_url',
+ vmwareapi_host_username='test_username',
+ vmwareapi_host_password='test_pass')
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
+ self.network = utils.import_object(FLAGS.network_manager)
+ vmwareapi_fake.reset()
+ db_fakes.stub_out_db_instance_api(self.stubs)
+ stubs.set_stubs(self.stubs)
+ glance_stubs.stubout_glance_client(self.stubs)
+ self.conn = vmwareapi_conn.get_connection(False)
+ # NOTE(vish): none of the network plugging code is actually
+ # being tested
+ self.network_info = [({'bridge': 'fa0',
+ 'id': 0,
+ 'vlan': None,
+ 'bridge_interface': None,
+ 'injected': True},
+ {'broadcast': '192.168.0.255',
+ 'dns': ['192.168.0.1'],
+ 'gateway': '192.168.0.1',
+ 'gateway6': 'dead:beef::1',
+ 'ip6s': [{'enabled': '1',
+ 'ip': 'dead:beef::dcad:beff:feef:0',
+ 'netmask': '64'}],
+ 'ips': [{'enabled': '1',
+ 'ip': '192.168.0.100',
+ 'netmask': '255.255.255.0'}],
+ 'label': 'fake',
+ 'mac': 'DE:AD:BE:EF:00:00',
+ 'rxtx_cap': 3})]
+
+ def tearDown(self):
+ super(VMWareAPIVMTestCase, self).tearDown()
+ vmwareapi_fake.cleanup()
def _create_instance_in_the_db(self):
values = {'name': 1,
'id': 1,
- 'project_id': self.project.id,
- 'user_id': self.user.id,
- 'image_id': "1",
+ 'project_id': self.project_id,
+ 'user_id': self.user_id,
+ 'image_ref': "1",
'kernel_id': "1",
'ramdisk_id': "1",
+ 'mac_address': "de:ad:be:ef:be:ef",
'instance_type': 'm1.large',
- 'mac_address': 'aa:bb:cc:dd:ee:ff',
}
- self.instance = db.instance_create(values)
+ self.instance = db.instance_create(None, values)
def _create_vm(self):
"""Create and spawn the VM."""
self._create_instance_in_the_db()
self.type_data = db.instance_type_get_by_name(None, 'm1.large')
- self.conn.spawn(self.instance)
+ self.conn.spawn(self.instance, self.network_info)
self._check_vm_record()
def _check_vm_record(self):
@@ -129,24 +138,20 @@ class VMWareAPIVMTestCase(test.TestCase):
self.assertEquals(info["mem"], mem_kib)
self.assertEquals(info["num_cpu"], self.type_data['vcpus'])
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_list_instances(self):
instances = self.conn.list_instances()
self.assertEquals(len(instances), 0)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_list_instances_1(self):
self._create_vm()
instances = self.conn.list_instances()
self.assertEquals(len(instances), 1)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_spawn(self):
self._create_vm()
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_snapshot(self):
self._create_vm()
info = self.conn.get_info(1)
@@ -155,27 +160,23 @@ class VMWareAPIVMTestCase(test.TestCase):
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_snapshot_non_existent(self):
self._create_instance_in_the_db()
self.assertRaises(Exception, self.conn.snapshot, self.instance,
"Test-Snapshot")
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_reboot(self):
self._create_vm()
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
- self.conn.reboot(self.instance)
+ self.conn.reboot(self.instance, self.network_info)
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_reboot_non_existent(self):
self._create_instance_in_the_db()
self.assertRaises(Exception, self.conn.reboot, self.instance)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_reboot_not_poweredon(self):
self._create_vm()
info = self.conn.get_info(1)
@@ -185,7 +186,6 @@ class VMWareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.PAUSED)
self.assertRaises(Exception, self.conn.reboot, self.instance)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_suspend(self):
self._create_vm()
info = self.conn.get_info(1)
@@ -194,13 +194,11 @@ class VMWareAPIVMTestCase(test.TestCase):
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.PAUSED)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_suspend_non_existent(self):
self._create_instance_in_the_db()
self.assertRaises(Exception, self.conn.suspend, self.instance,
self.dummy_callback_handler)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_resume(self):
self._create_vm()
info = self.conn.get_info(1)
@@ -212,13 +210,11 @@ class VMWareAPIVMTestCase(test.TestCase):
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_resume_non_existent(self):
self._create_instance_in_the_db()
self.assertRaises(Exception, self.conn.resume, self.instance,
self.dummy_callback_handler)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_resume_not_suspended(self):
self._create_vm()
info = self.conn.get_info(1)
@@ -226,49 +222,41 @@ class VMWareAPIVMTestCase(test.TestCase):
self.assertRaises(Exception, self.conn.resume, self.instance,
self.dummy_callback_handler)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_get_info(self):
self._create_vm()
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_destroy(self):
self._create_vm()
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
instances = self.conn.list_instances()
self.assertEquals(len(instances), 1)
- self.conn.destroy(self.instance)
+ self.conn.destroy(self.instance, self.network_info)
instances = self.conn.list_instances()
self.assertEquals(len(instances), 0)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_destroy_non_existent(self):
self._create_instance_in_the_db()
- self.assertEquals(self.conn.destroy(self.instance), None)
+ self.assertEquals(self.conn.destroy(self.instance, self.network_info),
+ None)
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_pause(self):
pass
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_unpause(self):
pass
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_diagnostics(self):
pass
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_get_console_output(self):
pass
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def test_get_ajax_console(self):
pass
- @test.skip_test("DB stubbing not removed, needs updating for multi-nic")
def dummy_callback_handler(self, ret):
"""
Dummy callback function to be passed to suspend, resume, etc., calls.
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 4cb7447d3..d4bca1281 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -30,7 +30,6 @@ from nova import flags
from nova import log as logging
from nova import test
from nova import utils
-from nova.auth import manager
from nova.compute import instance_types
from nova.compute import power_state
from nova import exception
@@ -69,7 +68,9 @@ class XenAPIVolumeTestCase(test.TestCase):
def setUp(self):
super(XenAPIVolumeTestCase, self).setUp()
self.stubs = stubout.StubOutForTesting()
- self.context = context.RequestContext('fake', 'fake', False)
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
FLAGS.target_host = '127.0.0.1'
FLAGS.xenapi_connection_url = 'test_url'
FLAGS.xenapi_connection_password = 'test_pass'
@@ -77,7 +78,7 @@ class XenAPIVolumeTestCase(test.TestCase):
stubs.stub_out_get_target(self.stubs)
xenapi_fake.reset()
self.values = {'id': 1,
- 'project_id': 'fake',
+ 'project_id': self.user_id,
'user_id': 'fake',
'image_ref': 1,
'kernel_id': 2,
@@ -173,10 +174,6 @@ class XenAPIVMTestCase(test.TestCase):
"""Unit tests for VM operations."""
def setUp(self):
super(XenAPIVMTestCase, self).setUp()
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('fake', 'fake', 'fake',
- admin=True)
- self.project = self.manager.create_project('fake', 'fake', 'fake')
self.network = utils.import_object(FLAGS.network_manager)
self.stubs = stubout.StubOutForTesting()
self.flags(xenapi_connection_url='test_url',
@@ -195,7 +192,9 @@ class XenAPIVMTestCase(test.TestCase):
stubs.stub_out_vm_methods(self.stubs)
glance_stubs.stubout_glance_client(self.stubs)
fake_utils.stub_out_utils_execute(self.stubs)
- self.context = context.RequestContext('fake', 'fake', False)
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
self.conn = xenapi_conn.get_connection(False)
def test_parallel_builds(self):
@@ -229,8 +228,8 @@ class XenAPIVMTestCase(test.TestCase):
instance = db.instance_create(self.context, values)
self.conn.spawn(instance, network_info)
- gt1 = eventlet.spawn(_do_build, 1, self.project.id, self.user.id)
- gt2 = eventlet.spawn(_do_build, 2, self.project.id, self.user.id)
+ gt1 = eventlet.spawn(_do_build, 1, self.project_id, self.user_id)
+ gt2 = eventlet.spawn(_do_build, 2, self.project_id, self.user_id)
gt1.wait()
gt2.wait()
@@ -396,18 +395,22 @@ class XenAPIVMTestCase(test.TestCase):
def _test_spawn(self, image_ref, kernel_id, ramdisk_id,
instance_type_id="3", os_type="linux",
architecture="x86-64", instance_id=1,
- check_injection=False):
+ check_injection=False,
+ create_record=True):
stubs.stubout_loopingcall_start(self.stubs)
- values = {'id': instance_id,
- 'project_id': self.project.id,
- 'user_id': self.user.id,
- 'image_ref': image_ref,
- 'kernel_id': kernel_id,
- 'ramdisk_id': ramdisk_id,
- 'instance_type_id': instance_type_id,
- 'os_type': os_type,
- 'architecture': architecture}
- instance = db.instance_create(self.context, values)
+ if create_record:
+ values = {'id': instance_id,
+ 'project_id': self.project_id,
+ 'user_id': self.user_id,
+ 'image_ref': image_ref,
+ 'kernel_id': kernel_id,
+ 'ramdisk_id': ramdisk_id,
+ 'instance_type_id': instance_type_id,
+ 'os_type': os_type,
+ 'architecture': architecture}
+ instance = db.instance_create(self.context, values)
+ else:
+ instance = db.instance_get(self.context, instance_id)
network_info = [({'bridge': 'fa0', 'id': 0, 'injected': True},
{'broadcast': '192.168.0.255',
'dns': ['192.168.0.1'],
@@ -465,12 +468,30 @@ class XenAPIVMTestCase(test.TestCase):
self._check_vdis(vdi_recs_start, vdi_recs_end)
def test_spawn_raw_objectstore(self):
- FLAGS.xenapi_image_service = 'objectstore'
- self._test_spawn(1, None, None)
+ # TODO(vish): deprecated
+ from nova.auth import manager
+ authman = manager.AuthManager()
+ authman.create_user('fake', 'fake')
+ authman.create_project('fake', 'fake')
+ try:
+ FLAGS.xenapi_image_service = 'objectstore'
+ self._test_spawn(1, None, None)
+ finally:
+ authman.delete_project('fake')
+ authman.delete_user('fake')
def test_spawn_objectstore(self):
- FLAGS.xenapi_image_service = 'objectstore'
- self._test_spawn(1, 2, 3)
+ # TODO(vish): deprecated
+ from nova.auth import manager
+ authman = manager.AuthManager()
+ authman.create_user('fake', 'fake')
+ authman.create_project('fake', 'fake')
+ try:
+ FLAGS.xenapi_image_service = 'objectstore'
+ self._test_spawn(1, 2, 3)
+ finally:
+ authman.delete_project('fake')
+ authman.delete_user('fake')
@stub_vm_utils_with_vdi_attached_here
def test_spawn_raw_glance(self):
@@ -599,41 +620,38 @@ class XenAPIVMTestCase(test.TestCase):
# guest agent is detected
self.assertFalse(self._tee_executed)
- @test.skip_test("Never gets an address, not sure why")
def test_spawn_vlanmanager(self):
self.flags(xenapi_image_service='glance',
network_manager='nova.network.manager.VlanManager',
- network_driver='nova.network.xenapi_net',
vlan_interface='fake0')
def dummy(*args, **kwargs):
pass
- self.stubs.Set(VMOps, 'create_vifs', dummy)
+ self.stubs.Set(vmops.VMOps, 'create_vifs', dummy)
# Reset network table
xenapi_fake.reset_table('network')
# Instance id = 2 will use vlan network (see db/fakes.py)
ctxt = self.context.elevated()
- instance_ref = self._create_instance(2)
- network_bk = self.network
- # Ensure we use xenapi_net driver
- self.network = utils.import_object(FLAGS.network_manager)
+ instance = self._create_instance(2, False)
networks = self.network.db.network_get_all(ctxt)
for network in networks:
- self.network.set_network_host(ctxt, network['id'])
-
- self.network.allocate_for_instance(ctxt, instance_id=instance_ref.id,
- instance_type_id=1, project_id=self.project.id)
- self.network.setup_compute_network(ctxt, instance_ref.id)
+ self.network.set_network_host(ctxt, network)
+
+ self.network.allocate_for_instance(ctxt,
+ instance_id=2,
+ host=FLAGS.host,
+ vpn=None,
+ instance_type_id=1,
+ project_id=self.project_id)
self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE,
glance_stubs.FakeGlance.IMAGE_KERNEL,
glance_stubs.FakeGlance.IMAGE_RAMDISK,
- instance_id=instance_ref.id,
+ instance_id=2,
create_record=False)
# TODO(salvatore-orlando): a complete test here would require
# a check for making sure the bridge for the VM's VIF is
# consistent with bridge specified in nova db
- self.network = network_bk
def test_spawn_with_network_qos(self):
self._create_instance()
@@ -644,10 +662,10 @@ class XenAPIVMTestCase(test.TestCase):
str(3 * 1024))
def test_rescue(self):
- self.flags(xenapi_inject_image=False)
+ self.flags(flat_injected=False)
instance = self._create_instance()
conn = xenapi_conn.get_connection(False)
- conn.rescue(instance, None)
+ conn.rescue(instance, None, [])
def test_unrescue(self):
instance = self._create_instance()
@@ -655,21 +673,13 @@ class XenAPIVMTestCase(test.TestCase):
# Ensure that it will not unrescue a non-rescued instance.
self.assertRaises(Exception, conn.unrescue, instance, None)
- def tearDown(self):
- super(XenAPIVMTestCase, self).tearDown()
- self.manager.delete_project(self.project)
- self.manager.delete_user(self.user)
- self.vm_info = None
- self.vm = None
- self.stubs.UnsetAll()
-
- def _create_instance(self, instance_id=1):
+ def _create_instance(self, instance_id=1, spawn=True):
"""Creates and spawns a test instance."""
stubs.stubout_loopingcall_start(self.stubs)
values = {
'id': instance_id,
- 'project_id': self.project.id,
- 'user_id': self.user.id,
+ 'project_id': self.project_id,
+ 'user_id': self.user_id,
'image_ref': 1,
'kernel_id': 2,
'ramdisk_id': 3,
@@ -691,7 +701,8 @@ class XenAPIVMTestCase(test.TestCase):
'label': 'fake',
'mac': 'DE:AD:BE:EF:00:00',
'rxtx_cap': 3})]
- self.conn.spawn(instance, network_info)
+ if spawn:
+ self.conn.spawn(instance, network_info)
return instance
@@ -750,14 +761,12 @@ class XenAPIMigrateInstance(test.TestCase):
stubs.stub_out_get_target(self.stubs)
xenapi_fake.reset()
xenapi_fake.create_network('fake', FLAGS.flat_network_bridge)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('fake', 'fake', 'fake',
- admin=True)
- self.project = self.manager.create_project('fake', 'fake', 'fake')
- self.context = context.RequestContext('fake', 'fake', False)
+ self.user_id = 'fake'
+ self.project_id = 'fake'
+ self.context = context.RequestContext(self.user_id, self.project_id)
self.values = {'id': 1,
- 'project_id': self.project.id,
- 'user_id': self.user.id,
+ 'project_id': self.project_id,
+ 'user_id': self.user_id,
'image_ref': 1,
'kernel_id': None,
'ramdisk_id': None,
@@ -771,20 +780,81 @@ class XenAPIMigrateInstance(test.TestCase):
stubs.stubout_get_this_vm_uuid(self.stubs)
glance_stubs.stubout_glance_client(self.stubs)
- def tearDown(self):
- super(XenAPIMigrateInstance, self).tearDown()
- self.manager.delete_project(self.project)
- self.manager.delete_user(self.user)
- self.stubs.UnsetAll()
-
def test_migrate_disk_and_power_off(self):
instance = db.instance_create(self.context, self.values)
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
conn = xenapi_conn.get_connection(False)
conn.migrate_disk_and_power_off(instance, '127.0.0.1')
- def test_finish_resize(self):
+ def test_finish_migrate(self):
+ instance = db.instance_create(self.context, self.values)
+ self.called = False
+
+ def fake_vdi_resize(*args, **kwargs):
+ self.called = True
+
+ self.stubs.Set(stubs.FakeSessionForMigrationTests,
+ "VDI_resize_online", fake_vdi_resize)
+ stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
+ stubs.stubout_loopingcall_start(self.stubs)
+ conn = xenapi_conn.get_connection(False)
+ network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False},
+ {'broadcast': '192.168.0.255',
+ 'dns': ['192.168.0.1'],
+ 'gateway': '192.168.0.1',
+ 'gateway6': 'dead:beef::1',
+ 'ip6s': [{'enabled': '1',
+ 'ip': 'dead:beef::dcad:beff:feef:0',
+ 'netmask': '64'}],
+ 'ips': [{'enabled': '1',
+ 'ip': '192.168.0.100',
+ 'netmask': '255.255.255.0'}],
+ 'label': 'fake',
+ 'mac': 'DE:AD:BE:EF:00:00',
+ 'rxtx_cap': 3})]
+ conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'),
+ network_info, resize_instance=True)
+ self.assertEqual(self.called, True)
+
+ def test_finish_migrate_no_local_storage(self):
+ tiny_type_id = \
+ instance_types.get_instance_type_by_name('m1.tiny')['id']
+ self.values.update({'instance_type_id': tiny_type_id, 'local_gb': 0})
+ instance = db.instance_create(self.context, self.values)
+
+ def fake_vdi_resize(*args, **kwargs):
+ raise Exception("This shouldn't be called")
+
+ self.stubs.Set(stubs.FakeSessionForMigrationTests,
+ "VDI_resize_online", fake_vdi_resize)
+ stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
+ stubs.stubout_loopingcall_start(self.stubs)
+ conn = xenapi_conn.get_connection(False)
+ network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False},
+ {'broadcast': '192.168.0.255',
+ 'dns': ['192.168.0.1'],
+ 'gateway': '192.168.0.1',
+ 'gateway6': 'dead:beef::1',
+ 'ip6s': [{'enabled': '1',
+ 'ip': 'dead:beef::dcad:beff:feef:0',
+ 'netmask': '64'}],
+ 'ips': [{'enabled': '1',
+ 'ip': '192.168.0.100',
+ 'netmask': '255.255.255.0'}],
+ 'label': 'fake',
+ 'mac': 'DE:AD:BE:EF:00:00',
+ 'rxtx_cap': 3})]
+ conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'),
+ network_info, resize_instance=True)
+
+ def test_finish_migrate_no_resize_vdi(self):
instance = db.instance_create(self.context, self.values)
+
+ def fake_vdi_resize(*args, **kwargs):
+ raise Exception("This shouldn't be called")
+
+ self.stubs.Set(stubs.FakeSessionForMigrationTests,
+ "VDI_resize_online", fake_vdi_resize)
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
stubs.stubout_loopingcall_start(self.stubs)
conn = xenapi_conn.get_connection(False)
@@ -802,8 +872,26 @@ class XenAPIMigrateInstance(test.TestCase):
'label': 'fake',
'mac': 'DE:AD:BE:EF:00:00',
'rxtx_cap': 3})]
- conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'),
- network_info)
+
+ # Resize instance would be determined by the compute call
+ conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'),
+ network_info, resize_instance=False)
+
+
+class XenAPIImageTypeTestCase(test.TestCase):
+ """Test ImageType class."""
+
+ def test_to_string(self):
+ """Can convert from type id to type string."""
+ self.assertEquals(
+ vm_utils.ImageType.to_string(vm_utils.ImageType.KERNEL),
+ vm_utils.ImageType.KERNEL_STR)
+
+ def test_from_string(self):
+ """Can convert from string to type id."""
+ self.assertEquals(
+ vm_utils.ImageType.from_string(vm_utils.ImageType.KERNEL_STR),
+ vm_utils.ImageType.KERNEL)
class XenAPIDetermineDiskImageTestCase(test.TestCase):
diff --git a/nova/tests/vmwareapi/db_fakes.py b/nova/tests/vmwareapi/db_fakes.py
index d4eb87daf..afd672c7a 100644
--- a/nova/tests/vmwareapi/db_fakes.py
+++ b/nova/tests/vmwareapi/db_fakes.py
@@ -70,8 +70,8 @@ def stub_out_db_instance_api(stubs):
'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
'instance_type': values['instance_type'],
'memory_mb': type_data['memory_mb'],
- 'mac_address': values['mac_address'],
'vcpus': type_data['vcpus'],
+ 'mac_addresses': [{'address': values['mac_address']}],
'local_gb': type_data['local_gb'],
}
return FakeModel(base_options)
@@ -83,6 +83,8 @@ def stub_out_db_instance_api(stubs):
'bridge': 'vmnet0',
'netmask': '255.255.255.0',
'gateway': '10.10.10.1',
+ 'broadcast': '10.10.10.255',
+ 'dns1': 'fake',
'vlan': 100}
return FakeModel(fields)
@@ -90,7 +92,7 @@ def stub_out_db_instance_api(stubs):
"""Stubs out the db.instance_action_create method."""
pass
- def fake_instance_get_fixed_address(context, instance_id):
+ def fake_instance_get_fixed_addresses(context, instance_id):
"""Stubs out the db.instance_get_fixed_address method."""
return '10.10.10.10'
@@ -103,7 +105,7 @@ def stub_out_db_instance_api(stubs):
stubs.Set(db, 'instance_create', fake_instance_create)
stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance)
stubs.Set(db, 'instance_action_create', fake_instance_action_create)
- stubs.Set(db, 'instance_get_fixed_address',
- fake_instance_get_fixed_address)
+ stubs.Set(db, 'instance_get_fixed_addresses',
+ fake_instance_get_fixed_addresses)
stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all)
stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name)
diff --git a/nova/tests/vmwareapi/stubs.py b/nova/tests/vmwareapi/stubs.py
index a648efb16..0ed5e9b68 100644
--- a/nova/tests/vmwareapi/stubs.py
+++ b/nova/tests/vmwareapi/stubs.py
@@ -22,6 +22,8 @@ Stubouts for the test suite
from nova.virt import vmwareapi_conn
from nova.virt.vmwareapi import fake
from nova.virt.vmwareapi import vmware_images
+from nova.virt.vmwareapi import vmops
+from nova.virt.vmwareapi import network_utils
def fake_get_vim_object(arg):
@@ -36,11 +38,16 @@ def fake_is_vim_object(arg, module):
def set_stubs(stubs):
"""Set the stubs."""
+ stubs.Set(vmops.VMWareVMOps, 'plug_vifs', fake.fake_plug_vifs)
+ stubs.Set(network_utils, 'get_network_with_the_name',
+ fake.fake_get_network)
stubs.Set(vmware_images, 'fetch_image', fake.fake_fetch_image)
stubs.Set(vmware_images, 'get_vmdk_size_and_properties',
fake.fake_get_vmdk_size_and_properties)
stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image)
stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object",
fake_get_vim_object)
+ stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object",
+ fake_get_vim_object)
stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object",
fake_is_vim_object)
diff --git a/nova/utils.py b/nova/utils.py
index 8784a227d..737903f81 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -19,7 +19,6 @@
"""Utilities and helper functions."""
-import base64
import datetime
import functools
import inspect
@@ -30,7 +29,6 @@ import os
import random
import re
import socket
-import string
import struct
import sys
import time
@@ -50,7 +48,8 @@ from nova import version
LOG = logging.getLogger("nova.utils")
-TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
+ISO_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
+PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
FLAGS = flags.FLAGS
@@ -361,16 +360,26 @@ def clear_time_override():
utcnow.override_time = None
-def isotime(at=None):
- """Returns iso formatted utcnow."""
+def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
+ """Returns formatted utcnow."""
if not at:
at = utcnow()
- return at.strftime(TIME_FORMAT)
+ return at.strftime(fmt)
+
+
+def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
+ """Turn a formatted time back into a datetime."""
+ return datetime.datetime.strptime(timestr, fmt)
+
+
+def isotime(at=None):
+ """Returns iso formatted utcnow."""
+ return strtime(at, ISO_TIME_FORMAT)
def parse_isotime(timestr):
"""Turn an iso formatted time back into a datetime."""
- return datetime.datetime.strptime(timestr, TIME_FORMAT)
+ return parse_strtime(timestr, ISO_TIME_FORMAT)
def parse_mailmap(mailmap='.mailmap'):
diff --git a/nova/virt/driver.py b/nova/virt/driver.py
index b2406e306..561115e3f 100644
--- a/nova/virt/driver.py
+++ b/nova/virt/driver.py
@@ -92,7 +92,7 @@ class ComputeDriver(object):
"""Launch a VM for the specified instance"""
raise NotImplementedError()
- def destroy(self, instance, cleanup=True):
+ def destroy(self, instance, network_info, cleanup=True):
"""Destroy (shutdown and delete) the specified instance.
The given parameter is an instance of nova.compute.service.Instance,
@@ -108,7 +108,7 @@ class ComputeDriver(object):
"""
raise NotImplementedError()
- def reboot(self, instance):
+ def reboot(self, instance, network_info):
"""Reboot specified VM"""
raise NotImplementedError()
@@ -149,11 +149,12 @@ class ComputeDriver(object):
"""Create snapshot from a running VM instance."""
raise NotImplementedError()
- def finish_resize(self, instance, disk_info):
+ def finish_migration(self, instance, disk_info, network_info,
+ resize_instance):
"""Completes a resize, turning on the migrated instance"""
raise NotImplementedError()
- def revert_resize(self, instance):
+ def revert_migration(self, instance):
"""Reverts a resize, powering back on the instance"""
raise NotImplementedError()
@@ -173,11 +174,11 @@ class ComputeDriver(object):
"""resume the specified instance"""
raise NotImplementedError()
- def rescue(self, instance, callback):
+ def rescue(self, instance, callback, network_info):
"""Rescue the specified instance"""
raise NotImplementedError()
- def unrescue(self, instance, callback):
+ def unrescue(self, instance, callback, network_info):
"""Unrescue the specified instance"""
raise NotImplementedError()
@@ -251,7 +252,7 @@ class ComputeDriver(object):
"""
raise NotImplementedError()
- def unfilter_instance(self, instance):
+ def unfilter_instance(self, instance, network_info):
"""Stop filtering instance"""
raise NotImplementedError()
@@ -280,3 +281,7 @@ class ComputeDriver(object):
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""
raise NotImplementedError()
+
+ def plug_vifs(self, instance, network_info):
+ """Plugs in VIFs to networks."""
+ raise NotImplementedError()
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index 48a03dac8..4957fbaf7 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -167,7 +167,7 @@ class FakeConnection(driver.ComputeDriver):
"""
pass
- def reboot(self, instance):
+ def reboot(self, instance, network_info):
"""
Reboot the specified instance.
@@ -240,13 +240,13 @@ class FakeConnection(driver.ComputeDriver):
"""
pass
- def rescue(self, instance):
+ def rescue(self, instance, callback, network_info):
"""
Rescue the specified instance.
"""
pass
- def unrescue(self, instance):
+ def unrescue(self, instance, callback, network_info):
"""
Unrescue the specified instance.
"""
@@ -293,7 +293,7 @@ class FakeConnection(driver.ComputeDriver):
"""
pass
- def destroy(self, instance):
+ def destroy(self, instance, network_info):
key = instance.name
if key in self.instances:
del self.instances[key]
@@ -499,7 +499,7 @@ class FakeConnection(driver.ComputeDriver):
"""This method is supported only by libvirt."""
return
- def unfilter_instance(self, instance_ref):
+ def unfilter_instance(self, instance_ref, network_info=None):
"""This method is supported only by libvirt."""
raise NotImplementedError('This method is supported only by libvirt.')
diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py
index f0ce71392..a97446c1a 100644
--- a/nova/virt/hyperv.py
+++ b/nova/virt/hyperv.py
@@ -66,7 +66,6 @@ import time
from nova import exception
from nova import flags
from nova import log as logging
-from nova.auth import manager
from nova.compute import power_state
from nova.virt import driver
from nova.virt import images
@@ -145,13 +144,12 @@ class HyperVConnection(driver.ComputeDriver):
if vm is not None:
raise exception.InstanceExists(name=instance.name)
- user = manager.AuthManager().get_user(instance['user_id'])
- project = manager.AuthManager().get_project(instance['project_id'])
#Fetch the file, assume it is a VHD file.
base_vhd_filename = os.path.join(FLAGS.instances_path,
instance.name)
vhdfile = "%s.vhd" % (base_vhd_filename)
- images.fetch(instance['image_ref'], vhdfile, user, project)
+ images.fetch(instance['image_ref'], vhdfile,
+ instance['user_id'], instance['project_id'])
try:
self._create_vm(instance)
@@ -368,14 +366,14 @@ class HyperVConnection(driver.ComputeDriver):
wmi_obj.Properties_.Item(prop).Value
return newinst
- def reboot(self, instance):
+ def reboot(self, instance, network_info):
"""Reboot the specified instance."""
vm = self._lookup(instance.name)
if vm is None:
raise exception.InstanceNotFound(instance_id=instance.id)
self._set_vm_state(instance.name, 'Reboot')
- def destroy(self, instance):
+ def destroy(self, instance, network_info):
"""Destroy the VM. Also destroy the associated VHD disk files"""
LOG.debug(_("Got request to destroy vm %s"), instance.name)
vm = self._lookup(instance.name)
diff --git a/nova/virt/images.py b/nova/virt/images.py
index 40bf6107c..2e9fca3d6 100644
--- a/nova/virt/images.py
+++ b/nova/virt/images.py
@@ -33,7 +33,7 @@ FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.virt.images')
-def fetch(image_href, path, _user, _project):
+def fetch(image_href, path, _user_id, _project_id):
# TODO(vish): Improve context handling and add owner and auth data
# when it is added to glance. Right now there is no
# auth checking in glance, so we assume that access was
diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template
index f7cf306bc..210e2b0fb 100644
--- a/nova/virt/libvirt.xml.template
+++ b/nova/virt/libvirt.xml.template
@@ -95,9 +95,13 @@
</disk>
#end if
#for $vol in $volumes
- <disk type='block'>
+ <disk type='${vol.type}'>
<driver type='raw'/>
+ #if $vol.type == 'network'
+ <source protocol='${vol.protocol}' name='${vol.name}'/>
+ #else
<source dev='${vol.device_path}'/>
+ #end if
<target dev='${vol.mount_device}' bus='${disk_bus}'/>
</disk>
#end for
@@ -105,6 +109,22 @@
#end if
#for $nic in $nics
+ #if $vif_type == 'ethernet'
+ <interface type='ethernet'>
+ <target dev='${nic.name}' />
+ <mac address='${nic.mac_address}' />
+ <script path='${nic.script}' />
+ </interface>
+ #else if $vif_type == '802.1Qbh'
+ <interface type='direct'>
+ <mac address='${nic.mac_address}'/>
+ <source dev='${nic.device_name}' mode='private'/>
+ <virtualport type='802.1Qbh'>
+ <parameters profileid='${nic.profile_name}'/>
+ </virtualport>
+ <model type='virtio'/>
+ </interface>
+ #else
<interface type='bridge'>
<source bridge='${nic.bridge_name}'/>
<mac address='${nic.mac_address}'/>
@@ -120,6 +140,8 @@
#end if
</filterref>
</interface>
+ #end if
+
#end for
<!-- The order is significant here. File must be defined first -->
<serial type="file">
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index cf013df30..03ab83cc0 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -124,6 +124,11 @@ flags.DEFINE_string('qemu_img', 'qemu-img',
'binary to use for qemu-img commands')
flags.DEFINE_bool('start_guests_on_host_boot', False,
'Whether to restart guests when the host reboots')
+flags.DEFINE_string('libvirt_vif_type', 'bridge',
+ 'Type of VIF to create.')
+flags.DEFINE_string('libvirt_vif_driver',
+ 'nova.virt.libvirt.vif.LibvirtBridgeDriver',
+ 'The libvirt VIF driver to configure the VIFs.')
def get_connection(read_only):
@@ -166,6 +171,7 @@ class LibvirtConnection(driver.ComputeDriver):
fw_class = utils.import_class(FLAGS.firewall_driver)
self.firewall_driver = fw_class(get_connection=self._get_connection)
+ self.vif_driver = utils.import_object(FLAGS.libvirt_vif_driver)
def init_host(self, host):
# Adopt existing VM's running here
@@ -257,7 +263,12 @@ class LibvirtConnection(driver.ComputeDriver):
infos.append(info)
return infos
- def destroy(self, instance, cleanup=True):
+ def plug_vifs(self, instance, network_info):
+ """Plugin VIFs into networks."""
+ for (network, mapping) in network_info:
+ self.vif_driver.plug(instance, network, mapping)
+
+ def destroy(self, instance, network_info, cleanup=True):
instance_name = instance['name']
try:
@@ -301,6 +312,9 @@ class LibvirtConnection(driver.ComputeDriver):
locals())
raise
+ for (network, mapping) in network_info:
+ self.vif_driver.unplug(instance, network, mapping)
+
def _wait_for_destroy():
"""Called at an interval until the VM is gone."""
instance_name = instance['name']
@@ -315,7 +329,8 @@ class LibvirtConnection(driver.ComputeDriver):
timer = utils.LoopingCall(_wait_for_destroy)
timer.start(interval=0.5, now=True)
- self.firewall_driver.unfilter_instance(instance)
+ self.firewall_driver.unfilter_instance(instance,
+ network_info=network_info)
if cleanup:
self._cleanup(instance)
@@ -336,24 +351,20 @@ class LibvirtConnection(driver.ComputeDriver):
def attach_volume(self, instance_name, device_path, mountpoint):
virt_dom = self._lookup_by_name(instance_name)
mount_device = mountpoint.rpartition("/")[2]
- if device_path.startswith('/dev/'):
+ (type, protocol, name) = \
+ self._get_volume_device_info(device_path)
+ if type == 'block':
xml = """<disk type='block'>
<driver name='qemu' type='raw'/>
<source dev='%s'/>
<target dev='%s' bus='virtio'/>
</disk>""" % (device_path, mount_device)
- elif ':' in device_path:
- (protocol, name) = device_path.split(':')
+ elif type == 'network':
xml = """<disk type='network'>
<driver name='qemu' type='raw'/>
<source protocol='%s' name='%s'/>
<target dev='%s' bus='virtio'/>
- </disk>""" % (protocol,
- name,
- mount_device)
- else:
- raise exception.InvalidDevicePath(path=device_path)
-
+ </disk>""" % (protocol, name, mount_device)
virt_dom.attachDevice(xml)
def _get_disk_xml(self, xml, device):
@@ -462,7 +473,7 @@ class LibvirtConnection(driver.ComputeDriver):
shutil.rmtree(temp_dir)
@exception.wrap_exception()
- def reboot(self, instance):
+ def reboot(self, instance, network_info):
"""Reboot a virtual machine, given an instance reference.
This method actually destroys and re-creates the domain to ensure the
@@ -477,7 +488,8 @@ class LibvirtConnection(driver.ComputeDriver):
# NOTE(itoumsn): self.shutdown() and wait instead of self.destroy() is
# better because we cannot ensure flushing dirty buffers
# in the guest OS. But, in case of KVM, shutdown() does not work...
- self.destroy(instance, False)
+ self.destroy(instance, network_info, cleanup=False)
+ self.plug_vifs(instance, network_info)
self.firewall_driver.setup_basic_filtering(instance)
self.firewall_driver.prepare_instance_filter(instance)
self._create_new_domain(xml)
@@ -527,7 +539,7 @@ class LibvirtConnection(driver.ComputeDriver):
dom.create()
@exception.wrap_exception()
- def rescue(self, instance):
+ def rescue(self, instance, callback, network_info):
"""Loads a VM using rescue images.
A rescue is normally performed when something goes wrong with the
@@ -536,7 +548,7 @@ class LibvirtConnection(driver.ComputeDriver):
data recovery.
"""
- self.destroy(instance, False)
+ self.destroy(instance, network_info, cleanup=False)
xml = self.to_xml(instance, rescue=True)
rescue_images = {'image_id': FLAGS.rescue_image_id,
@@ -565,14 +577,14 @@ class LibvirtConnection(driver.ComputeDriver):
return timer.start(interval=0.5, now=True)
@exception.wrap_exception()
- def unrescue(self, instance):
+ def unrescue(self, instance, network_info):
"""Reboot the VM which is being rescued back into primary images.
Because reboot destroys and re-creates instances, unresue should
simply call reboot.
"""
- self.reboot(instance)
+ self.reboot(instance, network_info)
@exception.wrap_exception()
def poll_rescued_instances(self, timeout):
@@ -757,9 +769,9 @@ class LibvirtConnection(driver.ComputeDriver):
else:
utils.execute('cp', base, target)
- def _fetch_image(self, target, image_id, user, project, size=None):
+ def _fetch_image(self, target, image_id, user_id, project_id, size=None):
"""Grab image and optionally attempt to resize it"""
- images.fetch(image_id, target, user, project)
+ images.fetch(image_id, target, user_id, project_id)
if size:
disk.extend(target, size)
@@ -806,9 +818,6 @@ class LibvirtConnection(driver.ComputeDriver):
os.close(os.open(basepath('console.log', ''),
os.O_CREAT | os.O_WRONLY, 0660))
- user = manager.AuthManager().get_user(inst['user_id'])
- project = manager.AuthManager().get_project(inst['project_id'])
-
if not disk_images:
disk_images = {'image_id': inst['image_ref'],
'kernel_id': inst['kernel_id'],
@@ -820,16 +829,16 @@ class LibvirtConnection(driver.ComputeDriver):
target=basepath('kernel'),
fname=fname,
image_id=disk_images['kernel_id'],
- user=user,
- project=project)
+ user_id=inst['user_id'],
+ project_id=inst['project_id'])
if disk_images['ramdisk_id']:
fname = '%08x' % int(disk_images['ramdisk_id'])
self._cache_image(fn=self._fetch_image,
target=basepath('ramdisk'),
fname=fname,
image_id=disk_images['ramdisk_id'],
- user=user,
- project=project)
+ user_id=inst['user_id'],
+ project_id=inst['project_id'])
root_fname = hashlib.sha1(disk_images['image_id']).hexdigest()
size = FLAGS.minimum_root_size
@@ -847,8 +856,8 @@ class LibvirtConnection(driver.ComputeDriver):
fname=root_fname,
cow=FLAGS.use_cow_images,
image_id=disk_images['image_id'],
- user=user,
- project=project,
+ user_id=inst['user_id'],
+ project_id=inst['project_id'],
size=size)
local_gb = inst['local_gb']
@@ -963,39 +972,6 @@ class LibvirtConnection(driver.ComputeDriver):
if FLAGS.libvirt_type == 'uml':
utils.execute('sudo', 'chown', 'root', basepath('disk'))
- def _get_nic_for_xml(self, network, mapping):
- # Assume that the gateway also acts as the dhcp server.
- gateway6 = mapping.get('gateway6')
- mac_id = mapping['mac'].replace(':', '')
-
- if FLAGS.allow_project_net_traffic:
- template = "<parameter name=\"%s\"value=\"%s\" />\n"
- net, mask = netutils.get_net_and_mask(network['cidr'])
- values = [("PROJNET", net), ("PROJMASK", mask)]
- if FLAGS.use_ipv6:
- net_v6, prefixlen_v6 = netutils.get_net_and_prefixlen(
- network['cidr_v6'])
- values.extend([("PROJNETV6", net_v6),
- ("PROJMASKV6", prefixlen_v6)])
-
- extra_params = "".join([template % value for value in values])
- else:
- extra_params = "\n"
-
- result = {
- 'id': mac_id,
- 'bridge_name': network['bridge'],
- 'mac_address': mapping['mac'],
- 'ip_address': mapping['ips'][0]['ip'],
- 'dhcp_server': mapping['dhcp_server'],
- 'extra_params': extra_params,
- }
-
- if gateway6:
- result['gateway6'] = gateway6 + "/128"
-
- return result
-
if FLAGS.libvirt_type == 'uml':
_disk_prefix = 'ubd'
elif FLAGS.libvirt_type == 'xen':
@@ -1026,6 +1002,15 @@ class LibvirtConnection(driver.ComputeDriver):
LOG.debug(_("block_device_list %s"), block_device_list)
return block_device.strip_dev(mount_device) in block_device_list
+ def _get_volume_device_info(self, device_path):
+ if device_path.startswith('/dev/'):
+ return ('block', None, None)
+ elif ':' in device_path:
+ (protocol, name) = device_path.split(':')
+ return ('network', protocol, name)
+ else:
+ raise exception.InvalidDevicePath(path=device_path)
+
def _prepare_xml_info(self, instance, rescue=False, network_info=None,
block_device_info=None):
block_device_mapping = driver.block_device_info_get_mapping(
@@ -1037,7 +1022,7 @@ class LibvirtConnection(driver.ComputeDriver):
nics = []
for (network, mapping) in network_info:
- nics.append(self._get_nic_for_xml(network, mapping))
+ nics.append(self.vif_driver.plug(instance, network, mapping))
# FIXME(vish): stick this in db
inst_type_id = instance['instance_type_id']
inst_type = instance_types.get_instance_type(inst_type_id)
@@ -1049,6 +1034,8 @@ class LibvirtConnection(driver.ComputeDriver):
for vol in block_device_mapping:
vol['mount_device'] = block_device.strip_dev(vol['mount_device'])
+ (vol['type'], vol['protocol'], vol['name']) = \
+ self._get_volume_device_info(vol['device_path'])
ebs_root = self._volume_in_mapping(self.default_root_device,
block_device_info)
@@ -1077,6 +1064,7 @@ class LibvirtConnection(driver.ComputeDriver):
'rescue': rescue,
'disk_prefix': self._disk_prefix,
'driver_type': driver_type,
+ 'vif_type': FLAGS.libvirt_vif_type,
'nics': nics,
'ebs_root': ebs_root,
'local_device': local_device,
@@ -1667,9 +1655,10 @@ class LibvirtConnection(driver.ComputeDriver):
timer.f = wait_for_live_migration
timer.start(interval=0.5, now=True)
- def unfilter_instance(self, instance_ref):
+ def unfilter_instance(self, instance_ref, network_info):
"""See comments of same method in firewall_driver."""
- self.firewall_driver.unfilter_instance(instance_ref)
+ self.firewall_driver.unfilter_instance(instance_ref,
+ network_info=network_info)
def update_host_status(self):
"""See xenapi_conn.py implementation."""
diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py
index 379197398..9ce57b6c9 100644
--- a/nova/virt/libvirt/firewall.py
+++ b/nova/virt/libvirt/firewall.py
@@ -46,7 +46,7 @@ class FirewallDriver(object):
At this point, the instance isn't running yet."""
raise NotImplementedError()
- def unfilter_instance(self, instance):
+ def unfilter_instance(self, instance, network_info=None):
"""Stop filtering instance"""
raise NotImplementedError()
@@ -300,9 +300,10 @@ class NWFilterFirewall(FirewallDriver):
# execute in a native thread and block current greenthread until done
tpool.execute(self._conn.nwfilterDefineXML, xml)
- def unfilter_instance(self, instance):
+ def unfilter_instance(self, instance, network_info=None):
"""Clear out the nwfilter rules."""
- network_info = netutils.get_network_info(instance)
+ if not network_info:
+ network_info = netutils.get_network_info(instance)
instance_name = instance.name
for (network, mapping) in network_info:
nic_id = mapping['mac'].replace(':', '')
@@ -542,11 +543,11 @@ class IptablesFirewallDriver(FirewallDriver):
"""No-op. Everything is done in prepare_instance_filter"""
pass
- def unfilter_instance(self, instance):
+ def unfilter_instance(self, instance, network_info=None):
if self.instances.pop(instance['id'], None):
self.remove_filters_for_instance(instance)
self.iptables.apply()
- self.nwfilter.unfilter_instance(instance)
+ self.nwfilter.unfilter_instance(instance, network_info)
else:
LOG.info(_('Attempted to unfilter instance %s which is not '
'filtered'), instance['id'])
diff --git a/nova/virt/libvirt/netutils.py b/nova/virt/libvirt/netutils.py
index 041eacb2d..a8e88fc07 100644
--- a/nova/virt/libvirt/netutils.py
+++ b/nova/virt/libvirt/netutils.py
@@ -25,6 +25,7 @@ import netaddr
from nova import context
from nova import db
+from nova import exception
from nova import flags
from nova import ipv6
from nova import utils
@@ -55,10 +56,12 @@ def get_network_info(instance):
# we should cache network_info
admin_context = context.get_admin_context()
- fixed_ips = db.fixed_ip_get_by_instance(admin_context, instance['id'])
+ try:
+ fixed_ips = db.fixed_ip_get_by_instance(admin_context, instance['id'])
+ except exception.FixedIpNotFoundForInstance:
+ fixed_ips = []
+
vifs = db.virtual_interface_get_by_instance(admin_context, instance['id'])
- networks = db.network_get_all_by_instance(admin_context,
- instance['id'])
flavor = db.instance_type_get(admin_context,
instance['instance_type_id'])
network_info = []
@@ -89,6 +92,7 @@ def get_network_info(instance):
'label': network['label'],
'gateway': network['gateway'],
'broadcast': network['broadcast'],
+ 'dhcp_server': network['gateway'],
'mac': vif['address'],
'rxtx_cap': flavor['rxtx_cap'],
'dns': [],
diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py
new file mode 100644
index 000000000..eef582fac
--- /dev/null
+++ b/nova/virt/libvirt/vif.py
@@ -0,0 +1,134 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (C) 2011 Midokura KK
+# Copyright (C) 2011 Nicira, Inc
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""VIF drivers for libvirt."""
+
+from nova import flags
+from nova import log as logging
+from nova.network import linux_net
+from nova.virt.libvirt import netutils
+from nova import utils
+from nova.virt.vif import VIFDriver
+
+LOG = logging.getLogger('nova.virt.libvirt.vif')
+
+FLAGS = flags.FLAGS
+
+flags.DEFINE_string('libvirt_ovs_bridge', 'br-int',
+ 'Name of Integration Bridge used by Open vSwitch')
+
+
+class LibvirtBridgeDriver(VIFDriver):
+ """VIF driver for Linux bridge."""
+
+ def _get_configurations(self, network, mapping):
+ """Get a dictionary of VIF configurations for bridge type."""
+ # Assume that the gateway also acts as the dhcp server.
+ gateway6 = mapping.get('gateway6')
+ mac_id = mapping['mac'].replace(':', '')
+
+ if FLAGS.allow_project_net_traffic:
+ template = "<parameter name=\"%s\"value=\"%s\" />\n"
+ net, mask = netutils.get_net_and_mask(network['cidr'])
+ values = [("PROJNET", net), ("PROJMASK", mask)]
+ if FLAGS.use_ipv6:
+ net_v6, prefixlen_v6 = netutils.get_net_and_prefixlen(
+ network['cidr_v6'])
+ values.extend([("PROJNETV6", net_v6),
+ ("PROJMASKV6", prefixlen_v6)])
+
+ extra_params = "".join([template % value for value in values])
+ else:
+ extra_params = "\n"
+
+ result = {
+ 'id': mac_id,
+ 'bridge_name': network['bridge'],
+ 'mac_address': mapping['mac'],
+ 'ip_address': mapping['ips'][0]['ip'],
+ 'dhcp_server': mapping['dhcp_server'],
+ 'extra_params': extra_params,
+ }
+
+ if gateway6:
+ result['gateway6'] = gateway6 + "/128"
+
+ return result
+
+ def plug(self, instance, network, mapping):
+ """Ensure that the bridge exists, and add VIF to it."""
+ if (not network.get('multi_host') and
+ mapping.get('should_create_bridge')):
+ if mapping.get('should_create_vlan'):
+ LOG.debug(_('Ensuring vlan %(vlan)s and bridge %(bridge)s'),
+ {'vlan': network['vlan'],
+ 'bridge': network['bridge']})
+ linux_net.ensure_vlan_bridge(network['vlan'],
+ network['bridge'],
+ network['bridge_interface'])
+ else:
+ LOG.debug(_("Ensuring bridge %s"), network['bridge'])
+ linux_net.ensure_bridge(network['bridge'],
+ network['bridge_interface'])
+
+ return self._get_configurations(network, mapping)
+
+ def unplug(self, instance, network, mapping):
+ """No manual unplugging required."""
+ pass
+
+
+class LibvirtOpenVswitchDriver(VIFDriver):
+ """VIF driver for Open vSwitch."""
+
+ def plug(self, instance, network, mapping):
+ vif_id = str(instance['id']) + "-" + str(network['id'])
+ dev = "tap-%s" % vif_id
+ iface_id = "nova-" + vif_id
+ if not linux_net._device_exists(dev):
+ utils.execute('sudo', 'ip', 'tuntap', 'add', dev, 'mode', 'tap')
+ utils.execute('sudo', 'ip', 'link', 'set', dev, 'up')
+ utils.execute('sudo', 'ovs-vsctl', '--', '--may-exist', 'add-port',
+ FLAGS.libvirt_ovs_bridge, dev,
+ '--', 'set', 'Interface', dev,
+ "external-ids:iface-id=%s" % iface_id,
+ '--', 'set', 'Interface', dev,
+ "external-ids:iface-status=active",
+ '--', 'set', 'Interface', dev,
+ "external-ids:attached-mac=%s" % mapping['mac'])
+
+ result = {
+ 'script': '',
+ 'name': dev,
+ 'mac_address': mapping['mac']}
+ return result
+
+ def unplug(self, instance, network, mapping):
+ """Unplug the VIF from the network by deleting the port from
+ the bridge."""
+ vif_id = str(instance['id']) + "-" + str(network['id'])
+ dev = "tap-%s" % vif_id
+ try:
+ utils.execute('sudo', 'ovs-vsctl', 'del-port',
+ network['bridge'], dev)
+ utils.execute('sudo', 'ip', 'link', 'delete', dev)
+ except:
+ LOG.warning(_("Failed while unplugging vif of instance '%s'"),
+ instance['name'])
+ raise
diff --git a/nova/virt/vif.py b/nova/virt/vif.py
new file mode 100644
index 000000000..b78689957
--- /dev/null
+++ b/nova/virt/vif.py
@@ -0,0 +1,30 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (C) 2011 Midokura KK
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""VIF module common to all virt layers."""
+
+
+class VIFDriver(object):
+ """Abstract class that defines generic interfaces for all VIF drivers."""
+
+ def plug(self, instance, network, mapping):
+ """Plug VIF into network."""
+ raise NotImplementedError()
+
+ def unplug(self, instance, network, mapping):
+ """Unplug VIF from network."""
+ raise NotImplementedError()
diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py
index 7370684bd..4c62d18bb 100644
--- a/nova/virt/vmwareapi/fake.py
+++ b/nova/virt/vmwareapi/fake.py
@@ -402,6 +402,16 @@ def _remove_file(file_path):
lst_files.remove(file)
+def fake_plug_vifs(*args, **kwargs):
+ """Fakes plugging vifs."""
+ pass
+
+
+def fake_get_network(*args, **kwargs):
+ """Fake get network."""
+ return [{'type': 'fake'}]
+
+
def fake_fetch_image(image, instance, **kwargs):
"""Fakes fetch image call. Just adds a reference to the db for the file."""
ds_name = kwargs.get("datastore_name")
diff --git a/nova/virt/vmwareapi/network_utils.py b/nova/virt/vmwareapi/network_utils.py
index e77842535..ec3b93fe7 100644
--- a/nova/virt/vmwareapi/network_utils.py
+++ b/nova/virt/vmwareapi/network_utils.py
@@ -45,10 +45,31 @@ def get_network_with_the_name(session, network_name="vmnet0"):
networks = session._call_method(vim_util,
"get_properties_for_a_collection_of_objects",
"Network", vm_networks, ["summary.name"])
- for network in networks:
- if network.propSet[0].val == network_name:
- return network.obj
- return None
+ network_obj = {}
+ LOG.warn(vm_networks)
+ for network in vm_networks:
+ # Get network properties
+ if network._type == 'DistributedVirtualPortgroup':
+ props = session._call_method(vim_util,
+ "get_dynamic_property", network,
+ "DistributedVirtualPortgroup", "config")
+ # NOTE(asomya): This only works on ESXi if the port binding is
+ # set to ephemeral
+ if props.name == network_name:
+ network_obj['type'] = 'DistributedVirtualPortgroup'
+ network_obj['dvpg'] = props.key
+ network_obj['dvsw'] = props.distributedVirtualSwitch.value
+ else:
+ props = session._call_method(vim_util,
+ "get_dynamic_property", network,
+ "Network", "summary.name")
+ if props == network_name:
+ network_obj['type'] = 'Network'
+ network_obj['name'] = network_name
+ if (len(network_obj) > 0):
+ return network_obj
+ else:
+ return None
def get_vswitch_for_vlan_interface(session, vlan_interface):
diff --git a/nova/virt/vmwareapi/vif.py b/nova/virt/vmwareapi/vif.py
new file mode 100644
index 000000000..b3e43b209
--- /dev/null
+++ b/nova/virt/vmwareapi/vif.py
@@ -0,0 +1,95 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Citrix Systems, Inc.
+# 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.
+
+"""VIF drivers for VMWare."""
+
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import utils
+from nova.virt.vif import VIFDriver
+from nova.virt.vmwareapi_conn import VMWareAPISession
+from nova.virt.vmwareapi import network_utils
+
+
+LOG = logging.getLogger("nova.virt.vmwareapi.vif")
+
+FLAGS = flags.FLAGS
+
+
+class VMWareVlanBridgeDriver(VIFDriver):
+ """VIF Driver to setup bridge/VLAN networking using VMWare API."""
+
+ def plug(self, instance, network, mapping):
+ """Create a vlan and bridge unless they already exist."""
+ vlan_num = network['vlan']
+ bridge = network['bridge']
+ bridge_interface = network['bridge_interface']
+
+ # Open vmwareapi session
+ host_ip = FLAGS.vmwareapi_host_ip
+ host_username = FLAGS.vmwareapi_host_username
+ host_password = FLAGS.vmwareapi_host_password
+ if not host_ip or host_username is None or host_password is None:
+ raise Exception(_('Must specify vmwareapi_host_ip, '
+ 'vmwareapi_host_username '
+ 'and vmwareapi_host_password to use '
+ 'connection_type=vmwareapi'))
+ session = VMWareAPISession(host_ip, host_username, host_password,
+ FLAGS.vmwareapi_api_retry_count)
+ vlan_interface = bridge_interface
+ # Check if the vlan_interface physical network adapter exists on the
+ # host.
+ if not network_utils.check_if_vlan_interface_exists(session,
+ vlan_interface):
+ raise exception.NetworkAdapterNotFound(adapter=vlan_interface)
+
+ # Get the vSwitch associated with the Physical Adapter
+ vswitch_associated = network_utils.get_vswitch_for_vlan_interface(
+ session, vlan_interface)
+ if vswitch_associated is None:
+ raise exception.SwicthNotFoundForNetworkAdapter(
+ adapter=vlan_interface)
+ # Check whether bridge already exists and retrieve the the ref of the
+ # network whose name_label is "bridge"
+ network_ref = network_utils.get_network_with_the_name(session, bridge)
+ if network_ref is None:
+ # Create a port group on the vSwitch associated with the
+ # vlan_interface corresponding physical network adapter on the ESX
+ # host.
+ network_utils.create_port_group(session, bridge,
+ vswitch_associated, vlan_num)
+ else:
+ # Get the vlan id and vswitch corresponding to the port group
+ pg_vlanid, pg_vswitch = \
+ network_utils.get_vlanid_and_vswitch_for_portgroup(session,
+ bridge)
+
+ # Check if the vswitch associated is proper
+ if pg_vswitch != vswitch_associated:
+ raise exception.InvalidVLANPortGroup(
+ bridge=bridge, expected=vswitch_associated,
+ actual=pg_vswitch)
+
+ # Check if the vlan id is proper for the port group
+ if pg_vlanid != vlan_num:
+ raise exception.InvalidVLANTag(bridge=bridge, tag=vlan_num,
+ pgroup=pg_vlanid)
+
+ def unplug(self, instance, network, mapping):
+ pass
diff --git a/nova/virt/vmwareapi/vm_util.py b/nova/virt/vmwareapi/vm_util.py
index 1638149f1..82b5f7214 100644
--- a/nova/virt/vmwareapi/vm_util.py
+++ b/nova/virt/vmwareapi/vm_util.py
@@ -40,7 +40,7 @@ def split_datastore_path(datastore_path):
def get_vm_create_spec(client_factory, instance, data_store_name,
network_name="vmnet0",
- os_type="otherGuest"):
+ os_type="otherGuest", network_ref=None):
"""Builds the VM Create spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
config_spec.name = instance.name
@@ -93,7 +93,8 @@ def create_controller_spec(client_factory, key):
return virtual_device_config
-def create_network_spec(client_factory, network_name, mac_address):
+def create_network_spec(client_factory, network_name, mac_address,
+ network_ref=None):
"""
Builds a config spec for the addition of a new network
adapter to the VM.
@@ -105,9 +106,25 @@ def create_network_spec(client_factory, network_name, mac_address):
# Get the recommended card type for the VM based on the guest OS of the VM
net_device = client_factory.create('ns0:VirtualPCNet32')
- backing = \
- client_factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
- backing.deviceName = network_name
+ # NOTE(asomya): Only works on ESXi if the portgroup binding is set to
+ # ephemeral. Invalid configuration if set to static and the NIC does
+ # not come up on boot if set to dynamic.
+ backing = None
+ if (network_ref and
+ network_ref['type'] == "DistributedVirtualPortgroup"):
+ backing_name = \
+ 'ns0:VirtualEthernetCardDistributedVirtualPortBackingInfo'
+ backing = \
+ client_factory.create(backing_name)
+ portgroup = \
+ client_factory.create('ns0:DistributedVirtualSwitchPortConnection')
+ portgroup.switchUuid = network_ref['dvsw']
+ portgroup.portgroupKey = network_ref['dvpg']
+ backing.port = portgroup
+ else:
+ backing = \
+ client_factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
+ backing.deviceName = network_name
connectable_spec = \
client_factory.create('ns0:VirtualDeviceConnectInfo')
@@ -278,9 +295,11 @@ def get_dummy_vm_create_spec(client_factory, name, data_store_name):
return config_spec
-def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask, gateway):
+def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask,
+ gateway, broadcast, dns):
"""Builds the machine id change config spec."""
- machine_id_str = "%s;%s;%s;%s" % (mac, ip_addr, netmask, gateway)
+ machine_id_str = "%s;%s;%s;%s;%s;%s" % (mac, ip_addr, netmask,
+ gateway, broadcast, dns)
virtual_machine_config_spec = \
client_factory.create('ns0:VirtualMachineConfigSpec')
diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py
index 94d9e6226..1ee8fa1c0 100644
--- a/nova/virt/vmwareapi/vmops.py
+++ b/nova/virt/vmwareapi/vmops.py
@@ -31,6 +31,7 @@ from nova import db
from nova import exception
from nova import flags
from nova import log as logging
+from nova import utils
from nova.compute import power_state
from nova.virt.vmwareapi import vim_util
from nova.virt.vmwareapi import vm_util
@@ -38,6 +39,10 @@ from nova.virt.vmwareapi import vmware_images
from nova.virt.vmwareapi import network_utils
FLAGS = flags.FLAGS
+flags.DEFINE_string('vmware_vif_driver',
+ 'nova.virt.vmwareapi.vif.VMWareVlanBridgeDriver',
+ 'The VMWare VIF driver to configure the VIFs.')
+
LOG = logging.getLogger("nova.virt.vmwareapi.vmops")
VMWARE_POWER_STATES = {
@@ -52,6 +57,7 @@ class VMWareVMOps(object):
def __init__(self, session):
"""Initializer."""
self._session = session
+ self._vif_driver = utils.import_object(FLAGS.vmware_vif_driver)
def _wait_with_callback(self, instance_id, task, callback):
"""Waits for the task to finish and does a callback after."""
@@ -83,7 +89,7 @@ class VMWareVMOps(object):
LOG.debug(_("Got total of %s instances") % str(len(lst_vm_names)))
return lst_vm_names
- def spawn(self, instance):
+ def spawn(self, instance, network_info):
"""
Creates a VM instance.
@@ -116,8 +122,10 @@ class VMWareVMOps(object):
net_name)
if network_ref is None:
raise exception.NetworkNotFoundForBridge(bridge=net_name)
+ return network_ref
- _check_if_network_bridge_exists()
+ self.plug_vifs(instance, network_info)
+ network_obj = _check_if_network_bridge_exists()
def _get_datastore_ref():
"""Get the datastore list and choose the first local storage."""
@@ -175,8 +183,10 @@ class VMWareVMOps(object):
vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors()
# Get the create vm config spec
- config_spec = vm_util.get_vm_create_spec(client_factory, instance,
- data_store_name, net_name, os_type)
+ config_spec = vm_util.get_vm_create_spec(
+ client_factory, instance,
+ data_store_name, net_name, os_type,
+ network_obj)
def _execute_create_vm():
"""Create VM on ESX host."""
@@ -472,11 +482,14 @@ class VMWareVMOps(object):
_clean_temp_data()
- def reboot(self, instance):
+ def reboot(self, instance, network_info):
"""Reboot a VM instance."""
vm_ref = self._get_vm_ref_from_the_name(instance.name)
if vm_ref is None:
raise exception.InstanceNotFound(instance_id=instance.id)
+
+ self.plug_vifs(instance, network_info)
+
lst_properties = ["summary.guest.toolsStatus", "runtime.powerState",
"summary.guest.toolsRunningStatus"]
props = self._session._call_method(vim_util, "get_object_properties",
@@ -514,7 +527,7 @@ class VMWareVMOps(object):
self._session._wait_for_task(instance.id, reset_task)
LOG.debug(_("Did hard reboot of VM %s") % instance.name)
- def destroy(self, instance):
+ def destroy(self, instance, network_info):
"""
Destroy a VM instance. Steps followed are:
1. Power off the VM, if it is in poweredOn state.
@@ -560,6 +573,8 @@ class VMWareVMOps(object):
LOG.warn(_("In vmwareapi:vmops:destroy, got this exception"
" while un-registering the VM: %s") % str(excep))
+ self._unplug_vifs(instance, network_info)
+
# Delete the folder holding the VM related content on
# the datastore.
try:
@@ -718,20 +733,25 @@ class VMWareVMOps(object):
net_mask = network["netmask"]
gateway = network["gateway"]
+ broadcast = network["broadcast"]
+ # TODO(vish): add support for dns2
+ dns = network["dns1"]
+
addresses = db.instance_get_fixed_addresses(admin_context,
instance['id'])
ip_addr = addresses[0] if addresses else None
- machine_id_chanfge_spec = \
+ machine_id_change_spec = \
vm_util.get_machine_id_change_spec(client_factory, mac_address,
- ip_addr, net_mask, gateway)
+ ip_addr, net_mask, gateway,
+ broadcast, dns)
LOG.debug(_("Reconfiguring VM instance %(name)s to set the machine id "
"with ip - %(ip_addr)s") %
({'name': instance.name,
'ip_addr': ip_addr}))
reconfig_task = self._session._call_method(self._session._get_vim(),
"ReconfigVM_Task", vm_ref,
- spec=machine_id_chanfge_spec)
+ spec=machine_id_change_spec)
self._session._wait_for_task(instance.id, reconfig_task)
LOG.debug(_("Reconfigured VM instance %(name)s to set the machine id "
"with ip - %(ip_addr)s") %
@@ -784,3 +804,13 @@ class VMWareVMOps(object):
if vm.propSet[0].val == vm_name:
return vm.obj
return None
+
+ def plug_vifs(self, instance, network_info):
+ """Plug VIFs into networks."""
+ for (network, mapping) in network_info:
+ self._vif_driver.plug(instance, network, mapping)
+
+ def _unplug_vifs(self, instance, network_info):
+ """Unplug VIFs from networks."""
+ for (network, mapping) in network_info:
+ self._vif_driver.unplug(instance, network, mapping)
diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py
index d80e14931..ce57847b2 100644
--- a/nova/virt/vmwareapi_conn.py
+++ b/nova/virt/vmwareapi_conn.py
@@ -124,21 +124,21 @@ class VMWareESXConnection(driver.ComputeDriver):
"""List VM instances."""
return self._vmops.list_instances()
- def spawn(self, instance, network_info=None, block_device_mapping=None):
+ def spawn(self, instance, network_info, block_device_mapping=None):
"""Create VM instance."""
- self._vmops.spawn(instance)
+ self._vmops.spawn(instance, network_info)
def snapshot(self, instance, name):
"""Create snapshot from a running VM instance."""
self._vmops.snapshot(instance, name)
- def reboot(self, instance):
+ def reboot(self, instance, network_info):
"""Reboot VM instance."""
- self._vmops.reboot(instance)
+ self._vmops.reboot(instance, network_info)
- def destroy(self, instance):
+ def destroy(self, instance, network_info):
"""Destroy VM instance."""
- self._vmops.destroy(instance)
+ self._vmops.destroy(instance, network_info)
def pause(self, instance, callback):
"""Pause VM instance."""
@@ -194,6 +194,10 @@ class VMWareESXConnection(driver.ComputeDriver):
"""Sets the specified host's ability to accept new instances."""
pass
+ def plug_vifs(self, instance, network_info):
+ """Plugs in VIFs to networks."""
+ self._vmops.plug_vifs(instance, network_info)
+
class VMWareAPISession(object):
"""
diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py
new file mode 100644
index 000000000..527602243
--- /dev/null
+++ b/nova/virt/xenapi/vif.py
@@ -0,0 +1,140 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Citrix Systems, Inc.
+# Copyright 2011 OpenStack LLC.
+# Copyright (C) 2011 Nicira, Inc
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""VIF drivers for XenAPI."""
+
+from nova import flags
+from nova import log as logging
+from nova.virt.vif import VIFDriver
+from nova.virt.xenapi.network_utils import NetworkHelper
+
+FLAGS = flags.FLAGS
+flags.DEFINE_string('xenapi_ovs_integration_bridge', 'xapi1',
+ 'Name of Integration Bridge used by Open vSwitch')
+
+LOG = logging.getLogger("nova.virt.xenapi.vif")
+
+
+class XenAPIBridgeDriver(VIFDriver):
+ """VIF Driver for XenAPI that uses XenAPI to create Networks."""
+
+ def plug(self, xenapi_session, vm_ref, instance, device, network,
+ network_mapping):
+ if network_mapping.get('should_create_vlan'):
+ network_ref = self.ensure_vlan_bridge(xenapi_session, network)
+ else:
+ network_ref = NetworkHelper.find_network_with_bridge(
+ xenapi_session, network['bridge'])
+ rxtx_cap = network_mapping.pop('rxtx_cap')
+ vif_rec = {}
+ vif_rec['device'] = str(device)
+ vif_rec['network'] = network_ref
+ vif_rec['VM'] = vm_ref
+ vif_rec['MAC'] = network_mapping['mac']
+ vif_rec['MTU'] = '1500'
+ vif_rec['other_config'] = {}
+ vif_rec['qos_algorithm_type'] = "ratelimit" if rxtx_cap else ''
+ vif_rec['qos_algorithm_params'] = \
+ {"kbps": str(rxtx_cap * 1024)} if rxtx_cap else {}
+ return vif_rec
+
+ def ensure_vlan_bridge(self, xenapi_session, network):
+ """Ensure that a VLAN bridge exists"""
+
+ vlan_num = network['vlan']
+ bridge = network['bridge']
+ bridge_interface = network['bridge_interface']
+ # Check whether bridge already exists
+ # Retrieve network whose name_label is "bridge"
+ network_ref = NetworkHelper.find_network_with_name_label(
+ xenapi_session, bridge)
+ if network_ref is None:
+ # If bridge does not exists
+ # 1 - create network
+ description = 'network for nova bridge %s' % bridge
+ network_rec = {'name_label': bridge,
+ 'name_description': description,
+ 'other_config': {}}
+ network_ref = xenapi_session.call_xenapi('network.create',
+ network_rec)
+ # 2 - find PIF for VLAN NOTE(salvatore-orlando): using double
+ # quotes inside single quotes as xapi filter only support
+ # tokens in double quotes
+ expr = 'field "device" = "%s" and \
+ field "VLAN" = "-1"' % bridge_interface
+ pifs = xenapi_session.call_xenapi('PIF.get_all_records_where',
+ expr)
+ pif_ref = None
+ # Multiple PIF are ok: we are dealing with a pool
+ if len(pifs) == 0:
+ raise Exception(_('Found no PIF for device %s') % \
+ bridge_interface)
+ for pif_ref in pifs.keys():
+ xenapi_session.call_xenapi('VLAN.create',
+ pif_ref,
+ str(vlan_num),
+ network_ref)
+ else:
+ # Check VLAN tag is appropriate
+ network_rec = xenapi_session.call_xenapi('network.get_record',
+ network_ref)
+ # Retrieve PIFs from network
+ for pif_ref in network_rec['PIFs']:
+ # Retrieve VLAN from PIF
+ pif_rec = xenapi_session.call_xenapi('PIF.get_record',
+ pif_ref)
+ pif_vlan = int(pif_rec['VLAN'])
+ # Raise an exception if VLAN != vlan_num
+ if pif_vlan != vlan_num:
+ raise Exception(_(
+ "PIF %(pif_rec['uuid'])s for network "
+ "%(bridge)s has VLAN id %(pif_vlan)d. "
+ "Expected %(vlan_num)d") % locals())
+
+ return network_ref
+
+ def unplug(self, instance, network, mapping):
+ pass
+
+
+class XenAPIOpenVswitchDriver(VIFDriver):
+ """VIF driver for Open vSwitch with XenAPI."""
+
+ def plug(self, xenapi_session, vm_ref, instance, device, network,
+ network_mapping):
+ # with OVS model, always plug into an OVS integration bridge
+ # that is already created
+ network_ref = NetworkHelper.find_network_with_bridge(xenapi_session,
+ FLAGS.xenapi_ovs_integration_bridge)
+ vif_rec = {}
+ vif_rec['device'] = str(device)
+ vif_rec['network'] = network_ref
+ vif_rec['VM'] = vm_ref
+ vif_rec['MAC'] = network_mapping['mac']
+ vif_rec['MTU'] = '1500'
+ vif_id = "nova-" + str(instance['id']) + "-" + str(network['id'])
+ vif_rec['qos_algorithm_type'] = ""
+ vif_rec['qos_algorithm_params'] = {}
+ # OVS on the hypervisor monitors this key and uses it to
+ # set the iface-id attribute
+ vif_rec['other_config'] = {"nicira-iface-id": vif_id}
+ return vif_rec
+
+ def unplug(self, instance, network, mapping):
+ pass
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 71107aff4..c9bcb801c 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -37,7 +37,6 @@ import nova.image
from nova.image import glance as glance_image_service
from nova import log as logging
from nova import utils
-from nova.auth.manager import AuthManager
from nova.compute import instance_types
from nova.compute import power_state
from nova.virt import disk
@@ -85,38 +84,22 @@ class ImageType:
DISK = 2
DISK_RAW = 3
DISK_VHD = 4
+ _ids = (KERNEL, RAMDISK, DISK, DISK_RAW, DISK_VHD)
KERNEL_STR = "kernel"
RAMDISK_STR = "ramdisk"
DISK_STR = "os"
DISK_RAW_STR = "os_raw"
DISK_VHD_STR = "vhd"
+ _strs = (KERNEL_STR, RAMDISK_STR, DISK_STR, DISK_RAW_STR, DISK_VHD_STR)
@classmethod
def to_string(cls, image_type):
- if image_type == ImageType.KERNEL:
- return ImageType.KERNEL_STR
- elif image_type == ImageType.RAMDISK:
- return ImageType.RAMDISK_STR
- elif image_type == ImageType.DISK:
- return ImageType.DISK_STR
- elif image_type == ImageType.DISK_RAW:
- return ImageType.DISK_RAW_STR
- elif image_type == ImageType.DISK_VHD:
- return ImageType.VHD_STR
+ return dict(zip(ImageType._ids, ImageType._strs)).get(image_type)
@classmethod
def from_string(cls, image_type_str):
- if image_type_str == ImageType.KERNEL_STR:
- return ImageType.KERNEL
- elif image_type == ImageType.RAMDISK_STR:
- return ImageType.RAMDISK
- elif image_type == ImageType.DISK_STR:
- return ImageType.DISK
- elif image_type == ImageType.DISK_RAW_STR:
- return ImageType.DISK_RAW
- elif image_type == ImageType.DISK_VHD_STR:
- return ImageType.VHD
+ return dict(zip(ImageType._strs, ImageType._ids)).get(image_type_str)
class VMHelper(HelperBase):
@@ -283,28 +266,6 @@ class VMHelper(HelperBase):
raise StorageError(_('Unable to destroy VDI %s') % vdi_ref)
@classmethod
- def create_vif(cls, session, vm_ref, network_ref, mac_address,
- dev, rxtx_cap=0):
- """Create a VIF record. Returns a Deferred that gives the new
- VIF reference."""
- vif_rec = {}
- vif_rec['device'] = str(dev)
- vif_rec['network'] = network_ref
- vif_rec['VM'] = vm_ref
- vif_rec['MAC'] = mac_address
- vif_rec['MTU'] = '1500'
- vif_rec['other_config'] = {}
- vif_rec['qos_algorithm_type'] = "ratelimit" if rxtx_cap else ''
- vif_rec['qos_algorithm_params'] = \
- {"kbps": str(rxtx_cap * 1024)} if rxtx_cap else {}
- LOG.debug(_('Creating VIF for VM %(vm_ref)s,'
- ' network %(network_ref)s.') % locals())
- vif_ref = session.call_xenapi('VIF.create', vif_rec)
- LOG.debug(_('Created VIF %(vif_ref)s for VM %(vm_ref)s,'
- ' network %(network_ref)s.') % locals())
- return vif_ref
-
- @classmethod
def create_vdi(cls, session, sr_ref, name_label, virtual_size, read_only):
"""Create a VDI record and returns its reference."""
vdi_ref = session.get_xenapi().VDI.create(
@@ -406,7 +367,7 @@ class VMHelper(HelperBase):
session.wait_for_task(task, instance.id)
@classmethod
- def fetch_image(cls, session, instance_id, image, user, project,
+ def fetch_image(cls, session, instance_id, image, user_id, project_id,
image_type):
"""
image_type is interpreted as an ImageType instance
@@ -418,18 +379,23 @@ class VMHelper(HelperBase):
Returns: A single filename if image_type is KERNEL_RAMDISK
A list of dictionaries that describe VDIs, otherwise
"""
- access = AuthManager().get_access_key(user, project)
if FLAGS.xenapi_image_service == 'glance':
- return cls._fetch_image_glance(session, instance_id, image,
- access, image_type)
+ return cls._fetch_image_glance(session, instance_id,
+ image, image_type)
else:
+ # TODO(vish): this shouldn't be used anywhere anymore and
+ # can probably be removed
+ from nova.auth.manager import AuthManager
+ manager = AuthManager()
+ access = manager.get_access_key(user_id, project_id)
+ secret = manager.get_user(user_id).secret
return cls._fetch_image_objectstore(session, instance_id, image,
- access, user.secret,
+ access, secret,
image_type)
@classmethod
- def _fetch_image_glance_vhd(cls, session, instance_id, image, access,
+ def _fetch_image_glance_vhd(cls, session, instance_id, image,
image_type):
"""Tell glance to download an image and put the VHDs into the SR
@@ -477,7 +443,7 @@ class VMHelper(HelperBase):
return vdis
@classmethod
- def _fetch_image_glance_disk(cls, session, instance_id, image, access,
+ def _fetch_image_glance_disk(cls, session, instance_id, image,
image_type):
"""Fetch the image from Glance
@@ -611,8 +577,7 @@ class VMHelper(HelperBase):
return image_type
@classmethod
- def _fetch_image_glance(cls, session, instance_id, image, access,
- image_type):
+ def _fetch_image_glance(cls, session, instance_id, image, image_type):
"""Fetch image from glance based on image type.
Returns: A single filename if image_type is KERNEL or RAMDISK
@@ -620,10 +585,10 @@ class VMHelper(HelperBase):
"""
if image_type == ImageType.DISK_VHD:
return cls._fetch_image_glance_vhd(
- session, instance_id, image, access, image_type)
+ session, instance_id, image, image_type)
else:
return cls._fetch_image_glance_disk(
- session, instance_id, image, access, image_type)
+ session, instance_id, image, image_type)
@classmethod
def _fetch_image_objectstore(cls, session, instance_id, image, access,
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 7995576a6..7e02e1def 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -38,7 +38,6 @@ from nova import ipv6
from nova import log as logging
from nova import utils
-from nova.auth.manager import AuthManager
from nova.compute import power_state
from nova.virt import driver
from nova.virt.xenapi.network_utils import NetworkHelper
@@ -52,6 +51,9 @@ FLAGS = flags.FLAGS
flags.DEFINE_integer('windows_version_timeout', 300,
'number of seconds to wait for windows agent to be '
'fully operational')
+flags.DEFINE_string('xenapi_vif_driver',
+ 'nova.virt.xenapi.vif.XenAPIBridgeDriver',
+ 'The XenAPI VIF driver using XenServer Network APIs.')
def cmp_version(a, b):
@@ -78,6 +80,7 @@ class VMOps(object):
self._session = session
self.poll_rescue_last_ran = None
VMHelper.XenAPI = self.XenAPI
+ self.vif_driver = utils.import_object(FLAGS.xenapi_vif_driver)
def list_instances(self):
"""List VM instances."""
@@ -106,17 +109,19 @@ class VMOps(object):
instance_infos.append(instance_info)
return instance_infos
- def revert_resize(self, instance):
+ def revert_migration(self, instance):
vm_ref = VMHelper.lookup(self._session, instance.name)
self._start(instance, vm_ref)
- def finish_resize(self, instance, disk_info, network_info):
+ def finish_migration(self, instance, disk_info, network_info,
+ resize_instance):
vdi_uuid = self.link_disks(instance, disk_info['base_copy'],
disk_info['cow'])
vm_ref = self._create_vm(instance,
[dict(vdi_type='os', vdi_uuid=vdi_uuid)],
network_info)
- self.resize_instance(instance, vdi_uuid)
+ if resize_instance:
+ self.resize_instance(instance, vdi_uuid)
self._spawn(instance, vm_ref)
def _start(self, instance, vm_ref=None):
@@ -130,11 +135,10 @@ class VMOps(object):
self._session.call_xenapi('VM.start', vm_ref, False, False)
def _create_disks(self, instance):
- user = AuthManager().get_user(instance.user_id)
- project = AuthManager().get_project(instance.project_id)
disk_image_type = VMHelper.determine_disk_image_type(instance)
vdis = VMHelper.fetch_image(self._session,
- instance.id, instance.image_ref, user, project,
+ instance.id, instance.image_ref,
+ instance.user_id, instance.project_id,
disk_image_type)
return vdis
@@ -172,21 +176,18 @@ class VMOps(object):
power_state.SHUTDOWN)
return
- user = AuthManager().get_user(instance.user_id)
- project = AuthManager().get_project(instance.project_id)
-
disk_image_type = VMHelper.determine_disk_image_type(instance)
kernel = None
ramdisk = None
try:
if instance.kernel_id:
kernel = VMHelper.fetch_image(self._session, instance.id,
- instance.kernel_id, user, project,
- ImageType.KERNEL)[0]
+ instance.kernel_id, instance.user_id,
+ instance.project_id, ImageType.KERNEL)[0]
if instance.ramdisk_id:
ramdisk = VMHelper.fetch_image(self._session, instance.id,
- instance.ramdisk_id, user, project,
- ImageType.RAMDISK)[0]
+ instance.kernel_id, instance.user_id,
+ instance.project_id, ImageType.RAMDISK)[0]
# Create the VM ref and attach the first disk
first_vdi_ref = self._session.call_xenapi('VDI.get_by_uuid',
vdis[0]['vdi_uuid'])
@@ -251,11 +252,11 @@ class VMOps(object):
userdevice += 1
# Alter the image before VM start for, e.g. network injection
- if FLAGS.xenapi_inject_image:
+ if FLAGS.flat_injected:
VMHelper.preconfigure_instance(self._session, instance,
first_vdi_ref, network_info)
- self.create_vifs(vm_ref, network_info)
+ self.create_vifs(vm_ref, instance, network_info)
self.inject_network_info(instance, network_info, vm_ref)
return vm_ref
@@ -564,18 +565,22 @@ class VMOps(object):
return new_cow_uuid
def resize_instance(self, instance, vdi_uuid):
- """Resize a running instance by changing it's RAM and disk size."""
+ """Resize a running instance by changing its RAM and disk size."""
#TODO(mdietz): this will need to be adjusted for swap later
#The new disk size must be in bytes
- new_disk_size = str(instance.local_gb * 1024 * 1024 * 1024)
- instance_name = instance.name
- instance_local_gb = instance.local_gb
- LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance %(instance_name)s."
- " Expanding to %(instance_local_gb)d GB") % locals())
- vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
- self._session.call_xenapi('VDI.resize_online', vdi_ref, new_disk_size)
- LOG.debug(_("Resize instance %s complete") % (instance.name))
+ new_disk_size = instance.local_gb * 1024 * 1024 * 1024
+ if new_disk_size > 0:
+ instance_name = instance.name
+ instance_local_gb = instance.local_gb
+ LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance"
+ "%(instance_name)s. Expanding to %(instance_local_gb)d"
+ " GB") % locals())
+ vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
+ # for an instance with no local storage
+ self._session.call_xenapi('VDI.resize_online', vdi_ref,
+ str(new_disk_size))
+ LOG.debug(_("Resize instance %s complete") % (instance.name))
def reboot(self, instance):
"""Reboot VM instance."""
@@ -837,7 +842,7 @@ class VMOps(object):
self._session.call_xenapi("Async.VM.destroy", rescue_vm_ref)
- def destroy(self, instance):
+ def destroy(self, instance, network_info):
"""Destroy VM instance.
This is the method exposed by xenapi_conn.destroy(). The rest of the
@@ -847,9 +852,9 @@ class VMOps(object):
instance_id = instance.id
LOG.info(_("Destroying VM for Instance %(instance_id)s") % locals())
vm_ref = VMHelper.lookup(self._session, instance.name)
- return self._destroy(instance, vm_ref, shutdown=True)
+ return self._destroy(instance, vm_ref, network_info, shutdown=True)
- def _destroy(self, instance, vm_ref, shutdown=True,
+ def _destroy(self, instance, vm_ref, network_info=None, shutdown=True,
destroy_kernel_ramdisk=True):
"""Destroys VM instance by performing:
@@ -871,6 +876,10 @@ class VMOps(object):
self._destroy_kernel_ramdisk(instance, vm_ref)
self._destroy_vm(instance, vm_ref)
+ if network_info:
+ for (network, mapping) in network_info:
+ self.vif_driver.unplug(instance, network, mapping)
+
def _wait_with_callback(self, instance_id, task, callback):
ret = None
try:
@@ -1066,7 +1075,7 @@ class VMOps(object):
# catch KeyError for domid if instance isn't running
pass
- def create_vifs(self, vm_ref, network_info):
+ def create_vifs(self, vm_ref, instance, network_info):
"""Creates vifs for an instance."""
logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
@@ -1075,14 +1084,19 @@ class VMOps(object):
self._session.get_xenapi().VM.get_record(vm_ref)
for device, (network, info) in enumerate(network_info):
- mac_address = info['mac']
- bridge = network['bridge']
- rxtx_cap = info.pop('rxtx_cap')
- network_ref = \
- NetworkHelper.find_network_with_bridge(self._session,
- bridge)
- VMHelper.create_vif(self._session, vm_ref, network_ref,
- mac_address, device, rxtx_cap)
+ vif_rec = self.vif_driver.plug(self._session,
+ vm_ref, instance, device, network, info)
+ network_ref = vif_rec['network']
+ LOG.debug(_('Creating VIF for VM %(vm_ref)s,' \
+ ' network %(network_ref)s.') % locals())
+ vif_ref = self._session.call_xenapi('VIF.create', vif_rec)
+ LOG.debug(_('Created VIF %(vif_ref)s for VM %(vm_ref)s,'
+ ' network %(network_ref)s.') % locals())
+
+ def plug_vifs(self, instance, network_info):
+ """Set up VIF networking on the host."""
+ for (network, mapping) in network_info:
+ self.vif_driver.plug(self._session, instance, network, mapping)
def reset_network(self, instance, vm_ref=None):
"""Creates uuid arg to pass to make_agent_call and calls it."""
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 4c6f9fe46..8704d1f86 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -112,22 +112,15 @@ flags.DEFINE_integer('xenapi_vhd_coalesce_max_attempts',
5,
'Max number of times to poll for VHD to coalesce.'
' Used only if connection_type=xenapi.')
-flags.DEFINE_bool('xenapi_inject_image',
- True,
- 'Specifies whether an attempt to inject network/key'
- ' data into the disk image should be made.'
- ' Used only if connection_type=xenapi.')
flags.DEFINE_string('xenapi_agent_path',
'usr/sbin/xe-update-networking',
'Specifies the path in which the xenapi guest agent'
' should be located. If the agent is present,'
' network configuration is not injected into the image'
' Used only if connection_type=xenapi.'
- ' and xenapi_inject_image=True')
-
+ ' and flat_injected=True')
flags.DEFINE_string('xenapi_sr_base_path', '/var/run/sr-mount',
'Base path to the storage repository')
-
flags.DEFINE_string('target_host',
None,
'iSCSI Target Host')
@@ -198,19 +191,21 @@ class XenAPIConnection(driver.ComputeDriver):
"""Create VM instance"""
self._vmops.spawn(instance, network_info)
- def revert_resize(self, instance):
+ def revert_migration(self, instance):
"""Reverts a resize, powering back on the instance"""
self._vmops.revert_resize(instance)
- def finish_resize(self, instance, disk_info, network_info):
+ def finish_migration(self, instance, disk_info, network_info,
+ resize_instance=False):
"""Completes a resize, turning on the migrated instance"""
- self._vmops.finish_resize(instance, disk_info, network_info)
+ self._vmops.finish_migration(instance, disk_info, network_info,
+ resize_instance)
def snapshot(self, instance, image_id):
""" Create snapshot from a running VM instance """
self._vmops.snapshot(instance, image_id)
- def reboot(self, instance):
+ def reboot(self, instance, network_info):
"""Reboot VM instance"""
self._vmops.reboot(instance)
@@ -224,9 +219,9 @@ class XenAPIConnection(driver.ComputeDriver):
"""
self._vmops.inject_file(instance, b64_path, b64_contents)
- def destroy(self, instance):
+ def destroy(self, instance, network_info):
"""Destroy VM instance"""
- self._vmops.destroy(instance)
+ self._vmops.destroy(instance, network_info)
def pause(self, instance, callback):
"""Pause VM instance"""
@@ -249,11 +244,11 @@ class XenAPIConnection(driver.ComputeDriver):
"""resume the specified instance"""
self._vmops.resume(instance, callback)
- def rescue(self, instance, callback):
+ def rescue(self, instance, callback, network_info):
"""Rescue the specified instance"""
self._vmops.rescue(instance, callback)
- def unrescue(self, instance, callback):
+ def unrescue(self, instance, callback, network_info):
"""Unrescue the specified instance"""
self._vmops.unrescue(instance, callback)
@@ -269,6 +264,9 @@ class XenAPIConnection(driver.ComputeDriver):
"""inject network info for specified instance"""
self._vmops.inject_network_info(instance, network_info)
+ def plug_vifs(self, instance_ref, network_info):
+ self._vmops.plug_vifs(instance_ref, network_info)
+
def get_info(self, instance_id):
"""Return data about VM instance"""
return self._vmops.get_info(instance_id)
@@ -322,7 +320,7 @@ class XenAPIConnection(driver.ComputeDriver):
"""This method is supported only by libvirt."""
return
- def unfilter_instance(self, instance_ref):
+ def unfilter_instance(self, instance_ref, network_info):
"""This method is supported only by libvirt."""
raise NotImplementedError('This method is supported only by libvirt.')
diff --git a/nova/volume/api.py b/nova/volume/api.py
index cfc274c77..52b3a9fed 100644
--- a/nova/volume/api.py
+++ b/nova/volume/api.py
@@ -52,7 +52,7 @@ class API(base.Base):
if quota.allowed_volumes(context, 1, size) < 1:
pid = context.project_id
- LOG.warn(_("Quota exceeeded for %(pid)s, tried to create"
+ LOG.warn(_("Quota exceeded for %(pid)s, tried to create"
" %(size)sG volume") % locals())
raise quota.QuotaError(_("Volume quota exceeded. You cannot "
"create a volume of size %sG") % size)
diff --git a/nova/wsgi.py b/nova/wsgi.py
index eae3afcb4..c8ddb97d7 100644
--- a/nova/wsgi.py
+++ b/nova/wsgi.py
@@ -274,6 +274,18 @@ class Middleware(Application):
return self.process_response(response)
+class InjectContext(Middleware):
+ """Add a 'nova.context' to WSGI environ."""
+ def __init__(self, context, *args, **kwargs):
+ self.context = context
+ super(InjectContext, self).__init__(*args, **kwargs)
+
+ @webob.dec.wsgify(RequestClass=Request)
+ def __call__(self, req):
+ req.environ['nova.context'] = self.context
+ return self.application
+
+
class Debug(Middleware):
"""Helper class for debugging a WSGI application.