summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorRick Harris <rick.harris@rackspace.com>2011-06-17 22:06:48 +0000
committerRick Harris <rick.harris@rackspace.com>2011-06-17 22:06:48 +0000
commitd20ade1e866d0e48c0724cd50e92fc84e068505a (patch)
treea2c76ca71c596355857cea880e1241c1fd5233ed /nova
parent1d815c177df76eb4f497a67fbdbd58fb170ca880 (diff)
parenta854d4e17214269b729777967e028600fdf4ee50 (diff)
Merging trunk
Diffstat (limited to 'nova')
-rw-r--r--nova/api/ec2/cloud.py3
-rw-r--r--nova/api/openstack/__init__.py3
-rw-r--r--nova/api/openstack/contrib/volumes.py6
-rw-r--r--nova/api/openstack/create_instance_helper.py19
-rw-r--r--nova/api/openstack/extensions.py13
-rw-r--r--nova/api/openstack/notes.txt3
-rw-r--r--nova/api/openstack/servers.py8
-rw-r--r--nova/api/openstack/wsgi.py8
-rw-r--r--nova/compute/api.py1
-rw-r--r--nova/db/sqlalchemy/api.py6
-rw-r--r--nova/flags.py4
-rw-r--r--nova/image/fake.py8
-rw-r--r--nova/image/local.py167
-rw-r--r--nova/scheduler/api.py5
-rw-r--r--nova/scheduler/zone_aware_scheduler.py13
-rw-r--r--nova/scheduler/zone_manager.py3
-rw-r--r--nova/tests/api/openstack/fakes.py1
-rw-r--r--nova/tests/api/openstack/test_extensions.py13
-rw-r--r--nova/tests/api/openstack/test_images.py30
-rw-r--r--nova/tests/fake_flags.py2
-rw-r--r--nova/tests/integrated/api/client.py16
-rw-r--r--nova/tests/test_cloud.py44
-rw-r--r--nova/tests/test_compute.py14
-rw-r--r--nova/tests/test_xenapi.py18
-rw-r--r--nova/virt/vmwareapi/vmware_images.py6
-rw-r--r--nova/virt/xenapi/fake.py1
-rw-r--r--nova/virt/xenapi/vm_utils.py35
27 files changed, 148 insertions, 302 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index e1c65ae40..c8a7e69be 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -974,7 +974,8 @@ class CloudController(object):
changes[field] = kwargs[field]
if changes:
instance_id = ec2utils.ec2_id_to_id(instance_id)
- self.compute_api.update(context, instance_id=instance_id, **kwargs)
+ self.compute_api.update(context, instance_id=instance_id,
+ **changes)
return True
@staticmethod
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index ddd9580d7..f24017df0 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -113,8 +113,7 @@ class APIRouter(base_wsgi.Router):
collection={'detail': 'GET',
'info': 'GET',
'select': 'POST',
- 'boot': 'POST'
- })
+ 'boot': 'POST'})
mapper.resource("console", "consoles",
controller=consoles.create_resource(),
diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py
index feabdce89..e5e2c5b50 100644
--- a/nova/api/openstack/contrib/volumes.py
+++ b/nova/api/openstack/contrib/volumes.py
@@ -301,7 +301,7 @@ class Volumes(extensions.ExtensionDescriptor):
return "Volumes"
def get_alias(self):
- return "VOLUMES"
+ return "os-volumes"
def get_description(self):
return "Volumes support"
@@ -317,12 +317,12 @@ class Volumes(extensions.ExtensionDescriptor):
# NOTE(justinsb): No way to provide singular name ('volume')
# Does this matter?
- res = extensions.ResourceExtension('volumes',
+ res = extensions.ResourceExtension('os-volumes',
VolumeController(),
collection_actions={'detail': 'GET'})
resources.append(res)
- res = extensions.ResourceExtension('volume_attachments',
+ res = extensions.ResourceExtension('os-volume_attachments',
VolumeAttachmentController(),
parent=dict(
member_name='server',
diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py
index fbc6318ef..436e524c1 100644
--- a/nova/api/openstack/create_instance_helper.py
+++ b/nova/api/openstack/create_instance_helper.py
@@ -94,7 +94,7 @@ class CreateInstanceHelper(object):
except Exception, e:
msg = _("Cannot find requested image %(image_href)s: %(e)s" %
locals())
- raise faults.Fault(exc.HTTPBadRequest(msg))
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
personality = body['server'].get('personality')
@@ -106,7 +106,7 @@ class CreateInstanceHelper(object):
if not 'name' in body['server']:
msg = _("Server name is not defined")
- raise exc.HTTPBadRequest(msg)
+ raise exc.HTTPBadRequest(explanation=msg)
zone_blob = body['server'].get('blob')
name = body['server']['name']
@@ -121,8 +121,7 @@ class CreateInstanceHelper(object):
extra_values = {
'instance_type': inst_type,
'image_ref': image_href,
- 'password': password
- }
+ 'password': password}
return (extra_values,
create_method(context,
@@ -138,14 +137,12 @@ class CreateInstanceHelper(object):
injected_files=injected_files,
admin_password=password,
zone_blob=zone_blob,
- reservation_id=reservation_id
- )
- )
+ reservation_id=reservation_id))
except quota.QuotaError as error:
self._handle_quota_error(error)
except exception.ImageNotFound as error:
msg = _("Can not find requested image")
- raise faults.Fault(exc.HTTPBadRequest(msg))
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
# Let the caller deal with unhandled exceptions.
@@ -180,11 +177,11 @@ class CreateInstanceHelper(object):
def _validate_server_name(self, value):
if not isinstance(value, basestring):
msg = _("Server name is not a string or unicode")
- raise exc.HTTPBadRequest(msg)
+ raise exc.HTTPBadRequest(explanation=msg)
if value.strip() == '':
msg = _("Server name is an empty string")
- raise exc.HTTPBadRequest(msg)
+ raise exc.HTTPBadRequest(explanation=msg)
def _get_kernel_ramdisk_from_image(self, req, image_id):
"""Fetch an image from the ImageService, then if present, return the
@@ -265,7 +262,7 @@ class CreateInstanceHelper(object):
return utils.generate_password(16)
if not isinstance(password, basestring) or password == '':
msg = _("Invalid adminPass")
- raise exc.HTTPBadRequest(msg)
+ raise exc.HTTPBadRequest(explanation=msg)
return password
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index 54e17e23d..da06ecd15 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -374,6 +374,8 @@ class ExtensionManager(object):
LOG.debug(_('Ext updated: %s'), extension.get_updated())
except AttributeError as ex:
LOG.exception(_("Exception loading extension: %s"), unicode(ex))
+ return False
+ return True
def _load_all_extensions(self):
"""Load extensions from the configured path.
@@ -412,15 +414,16 @@ class ExtensionManager(object):
'file': ext_path})
continue
new_ext = new_ext_class()
- self._check_extension(new_ext)
- self._add_extension(new_ext)
+ self.add_extension(new_ext)
+
+ def add_extension(self, ext):
+ # Do nothing if the extension doesn't check out
+ if not self._check_extension(ext):
+ return
- def _add_extension(self, ext):
alias = ext.get_alias()
LOG.audit(_('Loaded extension: %s'), alias)
- self._check_extension(ext)
-
if alias in self.extensions:
raise exception.Error("Found duplicate extension: %s" % alias)
self.extensions[alias] = ext
diff --git a/nova/api/openstack/notes.txt b/nova/api/openstack/notes.txt
index 2330f1002..4e95bffc8 100644
--- a/nova/api/openstack/notes.txt
+++ b/nova/api/openstack/notes.txt
@@ -7,9 +7,6 @@ image ids.
GlanceImageService(ImageService):
image ids are URIs.
-LocalImageService(ImageService):
-image ids are random strings.
-
OpenstackAPITranslationStore:
translates RS server/images/flavor/etc ids into formats required
by a given ImageService strategy.
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 798fdd7f7..b82a6de19 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -51,7 +51,7 @@ class Controller(object):
try:
servers = self._items(req, is_detail=False)
except exception.Invalid as err:
- return exc.HTTPBadRequest(str(err))
+ return exc.HTTPBadRequest(explanation=str(err))
return servers
def detail(self, req):
@@ -59,7 +59,7 @@ class Controller(object):
try:
servers = self._items(req, is_detail=True)
except exception.Invalid as err:
- return exc.HTTPBadRequest(str(err))
+ return exc.HTTPBadRequest(explanation=str(err))
return servers
def _get_view_builder(self, req):
@@ -488,11 +488,11 @@ class ControllerV11(Controller):
if (not 'changePassword' in input_dict
or not 'adminPass' in input_dict['changePassword']):
msg = _("No adminPass was specified")
- return exc.HTTPBadRequest(msg)
+ return exc.HTTPBadRequest(explanation=msg)
password = input_dict['changePassword']['adminPass']
if not isinstance(password, basestring) or password == '':
msg = _("Invalid adminPass")
- return exc.HTTPBadRequest(msg)
+ return exc.HTTPBadRequest(explanation=msg)
self.compute_api.set_admin_password(context, id, password)
return exc.HTTPAccepted()
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index 3f8acf339..a57b7f72b 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -363,11 +363,11 @@ class Resource(wsgi.Application):
action, action_args, accept = self.deserializer.deserialize(
request)
except exception.InvalidContentType:
- return webob.exc.HTTPBadRequest(_("Unsupported Content-Type"))
+ msg = _("Unsupported Content-Type")
+ return webob.exc.HTTPBadRequest(explanation=msg)
except exception.MalformedRequestBody:
- explanation = _("Malformed request body")
- return faults.Fault(webob.exc.HTTPBadRequest(
- explanation=explanation))
+ msg = _("Malformed request body")
+ return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg))
action_result = self.dispatch(request, action, action_args)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 990ef962b..717d9d730 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -533,6 +533,7 @@ class API(base.Base):
"""Get all instances with this reservation_id, across
all available Zones (if any).
"""
+ context = context.elevated()
instances = self.db.instance_get_all_by_reservation(
context, reservation_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 67e4cf7f0..91ceb435e 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -918,6 +918,7 @@ def instance_get_all_by_host(context, host):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
+ options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(host=host).\
filter_by(deleted=can_read_deleted(context)).\
@@ -933,6 +934,7 @@ def instance_get_all_by_project(context, project_id):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
+ options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(project_id=project_id).\
filter_by(deleted=can_read_deleted(context)).\
@@ -948,6 +950,7 @@ def instance_get_all_by_reservation(context, reservation_id):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
+ options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(reservation_id=reservation_id).\
filter_by(deleted=can_read_deleted(context)).\
@@ -957,6 +960,7 @@ def instance_get_all_by_reservation(context, reservation_id):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
+ options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(project_id=context.project_id).\
filter_by(reservation_id=reservation_id).\
@@ -970,6 +974,8 @@ def instance_get_project_vpn(context, project_id):
return session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ip.network')).\
+ options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(project_id=project_id).\
filter_by(image_ref=str(FLAGS.vpn_image_id)).\
diff --git a/nova/flags.py b/nova/flags.py
index acfcf8d68..57a4ecf2f 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -272,7 +272,7 @@ DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
# NOTE(sirp): my_ip interpolation doesn't work within nested structures
DEFINE_list('glance_api_servers',
- ['127.0.0.1:9292'],
+ ['%s:9292' % _get_my_ip()],
'list of glance api servers available to nova (host:port)')
DEFINE_integer('s3_port', 3333, 's3 port')
DEFINE_string('s3_host', '$my_ip', 's3 host (for infrastructure)')
@@ -364,7 +364,7 @@ DEFINE_string('scheduler_manager', 'nova.scheduler.manager.SchedulerManager',
'Manager for scheduler')
# The service to use for image search and retrieval
-DEFINE_string('image_service', 'nova.image.local.LocalImageService',
+DEFINE_string('image_service', 'nova.image.glance.GlanceImageService',
'The service to use for retrieving and searching for images.')
DEFINE_string('host', socket.gethostname(),
diff --git a/nova/image/fake.py b/nova/image/fake.py
index 70a5f0e22..c4b3d5fd6 100644
--- a/nova/image/fake.py
+++ b/nova/image/fake.py
@@ -120,6 +120,14 @@ class _FakeImageService(service.BaseImageService):
image_id, self.images)
raise exception.ImageNotFound(image_id=image_id)
+ def show_by_name(self, context, name):
+ """Returns a dict containing image data for the given name."""
+ images = copy.deepcopy(self.images.values())
+ for image in images:
+ if name == image.get('name'):
+ return image
+ raise exception.ImageNotFound(image_id=name)
+
def create(self, context, metadata, data=None):
"""Store the image data and return the new image id.
diff --git a/nova/image/local.py b/nova/image/local.py
deleted file mode 100644
index c7dee4573..000000000
--- a/nova/image/local.py
+++ /dev/null
@@ -1,167 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2010 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.
-
-import json
-import os.path
-import random
-import shutil
-
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import utils
-from nova.image import service
-
-
-FLAGS = flags.FLAGS
-flags.DEFINE_string('images_path', '$state_path/images',
- 'path to decrypted images')
-
-
-LOG = logging.getLogger('nova.image.local')
-
-
-class LocalImageService(service.BaseImageService):
- """Image service storing images to local disk.
-
- It assumes that image_ids are integers.
-
- """
-
- def __init__(self):
- self._path = FLAGS.images_path
-
- def _path_to(self, image_id, fname='info.json'):
- if fname:
- return os.path.join(self._path, '%08x' % int(image_id), fname)
- return os.path.join(self._path, '%08x' % int(image_id))
-
- def _ids(self):
- """The list of all image ids."""
- images = []
- for image_dir in os.listdir(self._path):
- try:
- unhexed_image_id = int(image_dir, 16)
- except ValueError:
- LOG.error(_('%s is not in correct directory naming format')
- % image_dir)
- else:
- images.append(unhexed_image_id)
- return images
-
- def index(self, context, filters=None, marker=None, limit=None):
- # TODO(blamar): Make use of filters, marker, and limit
- filtered = []
- image_metas = self.detail(context)
- for image_meta in image_metas:
- meta = utils.subset_dict(image_meta, ('id', 'name'))
- filtered.append(meta)
- return filtered
-
- def detail(self, context, filters=None, marker=None, limit=None):
- # TODO(blamar): Make use of filters, marker, and limit
- images = []
- for image_id in self._ids():
- try:
- image = self.show(context, image_id)
- images.append(image)
- except exception.NotFound:
- continue
- return images
-
- def show(self, context, image_id):
- try:
- with open(self._path_to(image_id)) as metadata_file:
- image_meta = json.load(metadata_file)
- if not self._is_image_available(context, image_meta):
- raise exception.ImageNotFound(image_id=image_id)
- return image_meta
- except (IOError, ValueError):
- raise exception.ImageNotFound(image_id=image_id)
-
- def show_by_name(self, context, name):
- """Returns a dict containing image data for the given name."""
- # NOTE(vish): Not very efficient, but the local image service
- # is for testing so it should be fine.
- images = self.detail(context)
- image = None
- for cantidate in images:
- if name == cantidate.get('name'):
- image = cantidate
- break
- if image is None:
- raise exception.ImageNotFound(image_id=name)
- return image
-
- def get(self, context, image_id, data):
- """Get image and metadata."""
- try:
- with open(self._path_to(image_id)) as metadata_file:
- metadata = json.load(metadata_file)
- with open(self._path_to(image_id, 'image')) as image_file:
- shutil.copyfileobj(image_file, data)
- except (IOError, ValueError):
- raise exception.ImageNotFound(image_id=image_id)
- return metadata
-
- def create(self, context, metadata, data=None):
- """Store the image data and return the new image."""
- image_id = random.randint(0, 2 ** 31 - 1)
- image_path = self._path_to(image_id, None)
- if not os.path.exists(image_path):
- os.mkdir(image_path)
- return self._store(context, image_id, metadata, data)
-
- def update(self, context, image_id, metadata, data=None):
- """Replace the contents of the given image with the new data."""
- # NOTE(vish): show is to check if image is available
- self.show(context, image_id)
- return self._store(context, image_id, metadata, data)
-
- def _store(self, context, image_id, metadata, data=None):
- metadata['id'] = image_id
- try:
- if data:
- location = self._path_to(image_id, 'image')
- with open(location, 'w') as image_file:
- shutil.copyfileobj(data, image_file)
- # NOTE(vish): update metadata similarly to glance
- metadata['status'] = 'active'
- metadata['location'] = location
- with open(self._path_to(image_id), 'w') as metadata_file:
- json.dump(metadata, metadata_file)
- except (IOError, ValueError):
- raise exception.ImageNotFound(image_id=image_id)
- return metadata
-
- def delete(self, context, image_id):
- """Delete the given image.
-
- :raises: ImageNotFound if the image does not exist.
-
- """
- # NOTE(vish): show is to check if image is available
- self.show(context, image_id)
- try:
- shutil.rmtree(self._path_to(image_id, None))
- except (IOError, ValueError):
- raise exception.ImageNotFound(image_id=image_id)
-
- def delete_all(self):
- """Clears out all images in local directory."""
- for image_id in self._ids():
- shutil.rmtree(self._path_to(image_id, None))
diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py
index 8ef893beb..1bb047e2e 100644
--- a/nova/scheduler/api.py
+++ b/nova/scheduler/api.py
@@ -107,7 +107,8 @@ def _wrap_method(function, self):
def _process(func, zone):
"""Worker stub for green thread pool. Give the worker
an authenticated nova client and zone info."""
- nova = novaclient.OpenStack(zone.username, zone.password, zone.api_url)
+ nova = novaclient.OpenStack(zone.username, zone.password, None,
+ zone.api_url)
nova.authenticate()
return func(nova, zone)
@@ -123,7 +124,7 @@ def call_zone_method(context, method_name, errors_to_ignore=None,
results = []
for zone in db.zone_get_all(context):
try:
- nova = novaclient.OpenStack(zone.username, zone.password,
+ nova = novaclient.OpenStack(zone.username, zone.password, None,
zone.api_url)
nova.authenticate()
except novaclient.exceptions.BadRequest, e:
diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py
index f04defa64..e7bff2faa 100644
--- a/nova/scheduler/zone_aware_scheduler.py
+++ b/nova/scheduler/zone_aware_scheduler.py
@@ -88,7 +88,7 @@ class ZoneAwareScheduler(driver.Scheduler):
instance_properties = request_spec['instance_properties']
name = instance_properties['display_name']
- image_id = instance_properties['image_id']
+ image_ref = instance_properties['image_ref']
meta = instance_properties['metadata']
flavor_id = instance_type['flavorid']
reservation_id = instance_properties['reservation_id']
@@ -105,13 +105,14 @@ class ZoneAwareScheduler(driver.Scheduler):
% locals())
nova = None
try:
- nova = novaclient.OpenStack(zone.username, zone.password, url)
+ nova = novaclient.OpenStack(zone.username, zone.password, None,
+ url)
nova.authenticate()
except novaclient.exceptions.BadRequest, e:
raise exception.NotAuthorized(_("Bad credentials attempting "
"to talk to zone at %(url)s.") % locals())
- nova.servers.create(name, image_id, flavor_id, ipgroup, meta, files,
+ nova.servers.create(name, image_ref, flavor_id, ipgroup, meta, files,
child_blob, reservation_id=reservation_id)
def _provision_resource_from_blob(self, context, item, instance_id,
@@ -184,7 +185,11 @@ class ZoneAwareScheduler(driver.Scheduler):
if not build_plan:
raise driver.NoValidHost(_('No hosts were available'))
- for item in build_plan:
+ for num in xrange(request_spec['num_instances']):
+ if not build_plan:
+ break
+
+ item = build_plan.pop(0)
self._provision_resource(context, item, instance_id, request_spec,
kwargs)
diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py
index 3f483adff..ba7403c15 100644
--- a/nova/scheduler/zone_manager.py
+++ b/nova/scheduler/zone_manager.py
@@ -89,7 +89,8 @@ class ZoneState(object):
def _call_novaclient(zone):
"""Call novaclient. Broken out for testing purposes."""
- client = novaclient.OpenStack(zone.username, zone.password, zone.api_url)
+ client = novaclient.OpenStack(zone.username, zone.password, None,
+ zone.api_url)
return client.zones.info()._info
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index a10fb7433..f8d158ddd 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -39,7 +39,6 @@ from nova.api.openstack import limits
from nova.auth.manager import User, Project
import nova.image.fake
from nova.image import glance
-from nova.image import local
from nova.image import service
from nova.tests import fake_flags
from nova.wsgi import Router
diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py
index 60914c0a3..697c62e5c 100644
--- a/nova/tests/api/openstack/test_extensions.py
+++ b/nova/tests/api/openstack/test_extensions.py
@@ -128,6 +128,11 @@ class ResourceExtensionTest(unittest.TestCase):
self.assertEqual(response_body, response.body)
+class InvalidExtension(object):
+ def get_alias(self):
+ return "THIRD"
+
+
class ExtensionManagerTest(unittest.TestCase):
response_body = "Try to say this Mr. Knox, sir..."
@@ -144,6 +149,14 @@ class ExtensionManagerTest(unittest.TestCase):
self.assertEqual(200, response.status_int)
self.assertEqual(response_body, response.body)
+ def test_invalid_extensions(self):
+ app = openstack.APIRouterV11()
+ ext_midware = extensions.ExtensionMiddleware(app)
+ ext_mgr = ext_midware.ext_mgr
+ ext_mgr.add_extension(InvalidExtension())
+ self.assertTrue('FOXNSOX' in ext_mgr.extensions)
+ self.assertTrue('THIRD' not in ext_mgr.extensions)
+
class ActionExtensionTest(unittest.TestCase):
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index be777df9b..e4204809f 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -135,36 +135,6 @@ class _BaseImageServiceTests(test.TestCase):
return fixture
-class LocalImageServiceTest(_BaseImageServiceTests):
-
- """Tests the local image service"""
-
- def setUp(self):
- super(LocalImageServiceTest, self).setUp()
- self.tempdir = tempfile.mkdtemp()
- self.flags(images_path=self.tempdir)
- self.stubs = stubout.StubOutForTesting()
- service_class = 'nova.image.local.LocalImageService'
- self.service = utils.import_object(service_class)
- self.context = context.RequestContext(None, None)
-
- def tearDown(self):
- shutil.rmtree(self.tempdir)
- self.stubs.UnsetAll()
- super(LocalImageServiceTest, self).tearDown()
-
- def test_get_all_ids_with_incorrect_directory_formats(self):
- # create some old-style image directories (starting with 'ami-')
- for x in [1, 2, 3]:
- tempfile.mkstemp(prefix='ami-', dir=self.tempdir)
- # create some valid image directories names
- for x in ["1485baed", "1a60f0ee", "3123a73d"]:
- os.makedirs(os.path.join(self.tempdir, x))
- found_image_ids = self.service._ids()
- self.assertEqual(True, isinstance(found_image_ids, list))
- self.assertEqual(3, len(found_image_ids), len(found_image_ids))
-
-
class GlanceImageServiceTest(_BaseImageServiceTests):
"""Tests the Glance image service, in particular that metadata translation
diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py
index ecefc464a..2297d2f0e 100644
--- a/nova/tests/fake_flags.py
+++ b/nova/tests/fake_flags.py
@@ -32,7 +32,7 @@ flags.DECLARE('fake_network', 'nova.network.manager')
FLAGS['network_size'].SetDefault(8)
FLAGS['num_networks'].SetDefault(2)
FLAGS['fake_network'].SetDefault(True)
-FLAGS['image_service'].SetDefault('nova.image.local.LocalImageService')
+FLAGS['image_service'].SetDefault('nova.image.fake.FakeImageService')
flags.DECLARE('num_shelves', 'nova.volume.driver')
flags.DECLARE('blades_per_shelf', 'nova.volume.driver')
flags.DECLARE('iscsi_num_targets', 'nova.volume.driver')
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index eb9a3056e..76c03c5fa 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -221,30 +221,30 @@ class TestOpenStackClient(object):
return self.api_delete('/flavors/%s' % flavor_id)
def get_volume(self, volume_id):
- return self.api_get('/volumes/%s' % volume_id)['volume']
+ return self.api_get('/os-volumes/%s' % volume_id)['volume']
def get_volumes(self, detail=True):
- rel_url = '/volumes/detail' if detail else '/volumes'
+ rel_url = '/os-volumes/detail' if detail else '/os-volumes'
return self.api_get(rel_url)['volumes']
def post_volume(self, volume):
- return self.api_post('/volumes', volume)['volume']
+ return self.api_post('/os-volumes', volume)['volume']
def delete_volume(self, volume_id):
- return self.api_delete('/volumes/%s' % volume_id)
+ return self.api_delete('/os-volumes/%s' % volume_id)
def get_server_volume(self, server_id, attachment_id):
- return self.api_get('/servers/%s/volume_attachments/%s' %
+ return self.api_get('/servers/%s/os-volume_attachments/%s' %
(server_id, attachment_id))['volumeAttachment']
def get_server_volumes(self, server_id):
- return self.api_get('/servers/%s/volume_attachments' %
+ return self.api_get('/servers/%s/os-volume_attachments' %
(server_id))['volumeAttachments']
def post_server_volume(self, server_id, volume_attachment):
- return self.api_post('/servers/%s/volume_attachments' %
+ return self.api_post('/servers/%s/os-volume_attachments' %
(server_id), volume_attachment)['volumeAttachment']
def delete_server_volume(self, server_id, attachment_id):
- return self.api_delete('/servers/%s/volume_attachments/%s' %
+ return self.api_delete('/servers/%s/os-volume_attachments/%s' %
(server_id, attachment_id))
diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py
index 13046f861..cbb6233ac 100644
--- a/nova/tests/test_cloud.py
+++ b/nova/tests/test_cloud.py
@@ -35,7 +35,7 @@ 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 local
+from nova.image import fake
FLAGS = flags.FLAGS
@@ -69,8 +69,8 @@ class CloudTestCase(test.TestCase):
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
'type': 'machine', 'image_state': 'available'}}
- self.stubs.Set(local.LocalImageService, 'show', fake_show)
- self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
+ self.stubs.Set(fake._FakeImageService, 'show', fake_show)
+ self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show)
# NOTE(vish): set up a manual wait so rpc.cast has a chance to finish
rpc_cast = rpc.cast
@@ -303,7 +303,7 @@ class CloudTestCase(test.TestCase):
def fake_show_none(meh, context, id):
raise exception.ImageNotFound(image_id='bad_image_id')
- self.stubs.Set(local.LocalImageService, 'detail', fake_detail)
+ self.stubs.Set(fake._FakeImageService, 'detail', fake_detail)
# list all
result1 = describe_images(self.context)
result1 = result1['imagesSet'][0]
@@ -317,8 +317,8 @@ class CloudTestCase(test.TestCase):
self.assertEqual(2, len(result3['imagesSet']))
# provide an non-existing image_id
self.stubs.UnsetAll()
- self.stubs.Set(local.LocalImageService, 'show', fake_show_none)
- self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show_none)
+ self.stubs.Set(fake._FakeImageService, 'show', fake_show_none)
+ self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show_none)
self.assertRaises(exception.ImageNotFound, describe_images,
self.context, ['ami-fake'])
@@ -329,8 +329,8 @@ class CloudTestCase(test.TestCase):
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
'type': 'machine'}, 'is_public': True}
- self.stubs.Set(local.LocalImageService, 'show', fake_show)
- self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
+ self.stubs.Set(fake._FakeImageService, 'show', fake_show)
+ self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show)
result = describe_image_attribute(self.context, 'ami-00000001',
'launchPermission')
self.assertEqual([{'group': 'all'}], result['launchPermission'])
@@ -345,9 +345,9 @@ class CloudTestCase(test.TestCase):
def fake_update(meh, context, image_id, metadata, data=None):
return metadata
- self.stubs.Set(local.LocalImageService, 'show', fake_show)
- self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
- self.stubs.Set(local.LocalImageService, 'update', fake_update)
+ self.stubs.Set(fake._FakeImageService, 'show', fake_show)
+ self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show)
+ self.stubs.Set(fake._FakeImageService, 'update', fake_update)
result = modify_image_attribute(self.context, 'ami-00000001',
'launchPermission', 'add',
user_group=['all'])
@@ -359,7 +359,7 @@ class CloudTestCase(test.TestCase):
def fake_delete(self, context, id):
return None
- self.stubs.Set(local.LocalImageService, 'delete', fake_delete)
+ self.stubs.Set(fake._FakeImageService, 'delete', fake_delete)
# valid image
result = deregister_image(self.context, 'ami-00000001')
self.assertEqual(result['imageId'], 'ami-00000001')
@@ -369,7 +369,7 @@ class CloudTestCase(test.TestCase):
def fake_detail_empty(self, context):
return []
- self.stubs.Set(local.LocalImageService, 'detail', fake_detail_empty)
+ self.stubs.Set(fake._FakeImageService, 'detail', fake_detail_empty)
self.assertRaises(exception.ImageNotFound, deregister_image,
self.context, 'ami-bad001')
@@ -457,6 +457,12 @@ 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}
@@ -466,7 +472,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'], 'networking')
+ self.assertEqual(instance['instanceState']['name'], 'scheduling')
self.assertEqual(instance['instanceType'], 'm1.small')
def test_run_instances_image_state_none(self):
@@ -480,7 +486,7 @@ class CloudTestCase(test.TestCase):
'type': 'machine'}}
self.stubs.UnsetAll()
- self.stubs.Set(local.LocalImageService, 'show', fake_show_no_state)
+ self.stubs.Set(fake._FakeImageService, 'show', fake_show_no_state)
self.assertRaises(exception.ApiError, run_instances,
self.context, **kwargs)
@@ -495,7 +501,7 @@ class CloudTestCase(test.TestCase):
'type': 'machine', 'image_state': 'decrypting'}}
self.stubs.UnsetAll()
- self.stubs.Set(local.LocalImageService, 'show', fake_show_decrypt)
+ self.stubs.Set(fake._FakeImageService, 'show', fake_show_decrypt)
self.assertRaises(exception.ApiError, run_instances,
self.context, **kwargs)
@@ -509,7 +515,7 @@ class CloudTestCase(test.TestCase):
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
'type': 'machine'}, 'status': 'active'}
- self.stubs.Set(local.LocalImageService, 'show', fake_show_stat_active)
+ self.stubs.Set(fake._FakeImageService, 'show', fake_show_stat_active)
result = run_instances(self.context, **kwargs)
self.assertEqual(len(result['instancesSet']), 1)
@@ -538,7 +544,9 @@ class CloudTestCase(test.TestCase):
def test_update_of_instance_wont_update_private_fields(self):
inst = db.instance_create(self.context, {})
- self.cloud.update_instance(self.context, inst['id'],
+ 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')
inst = db.instance_get(self.context, inst['id'])
self.assertEqual(None, inst['mac_address'])
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index b4ac2dbc4..2a68df2fc 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -22,21 +22,21 @@ 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
+from nova.compute import power_state
from nova import context
from nova import db
+from nova.db.sqlalchemy import models
from nova import exception
from nova import flags
+import nova.image.fake
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.compute import instance_types
-from nova.compute import manager as compute_manager
-from nova.compute import power_state
-from nova.db.sqlalchemy import models
-from nova.image import local
LOG = logging.getLogger('nova.tests.compute')
FLAGS = flags.FLAGS
@@ -73,7 +73,7 @@ class ComputeTestCase(test.TestCase):
def fake_show(meh, context, id):
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
- self.stubs.Set(local.LocalImageService, 'show', fake_show)
+ self.stubs.Set(nova.image.fake._FakeImageService, 'show', fake_show)
def tearDown(self):
self.manager.delete_user(self.user)
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index d1c88287a..c0213250a 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -33,6 +33,7 @@ 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
from nova.virt import xenapi_conn
from nova.virt.xenapi import fake as xenapi_fake
from nova.virt.xenapi import volume_utils
@@ -228,6 +229,23 @@ class XenAPIVMTestCase(test.TestCase):
instance = self._create_instance()
self.conn.get_diagnostics(instance)
+ def test_instance_snapshot_fails_with_no_primary_vdi(self):
+ def create_bad_vbd(vm_ref, vdi_ref):
+ vbd_rec = {'VM': vm_ref,
+ 'VDI': vdi_ref,
+ 'userdevice': 'fake',
+ 'currently_attached': False}
+ vbd_ref = xenapi_fake._create_object('VBD', vbd_rec)
+ xenapi_fake.after_VBD_create(vbd_ref, vbd_rec)
+ return vbd_ref
+
+ self.stubs.Set(xenapi_fake, 'create_vbd', create_bad_vbd)
+ stubs.stubout_instance_snapshot(self.stubs)
+ instance = self._create_instance()
+
+ name = "MySnapshot"
+ self.assertRaises(exception.Error, self.conn.snapshot, instance, name)
+
def test_instance_snapshot(self):
stubs.stubout_instance_snapshot(self.stubs)
instance = self._create_instance()
diff --git a/nova/virt/vmwareapi/vmware_images.py b/nova/virt/vmwareapi/vmware_images.py
index 48edc5384..70adba74f 100644
--- a/nova/virt/vmwareapi/vmware_images.py
+++ b/nova/virt/vmwareapi/vmware_images.py
@@ -90,8 +90,6 @@ def fetch_image(image, instance, **kwargs):
func = _get_glance_image
elif FLAGS.image_service == "nova.image.s3.S3ImageService":
func = _get_s3_image
- elif FLAGS.image_service == "nova.image.local.LocalImageService":
- func = _get_local_image
else:
raise NotImplementedError(_("The Image Service %s is not implemented")
% FLAGS.image_service)
@@ -105,8 +103,6 @@ def upload_image(image, instance, **kwargs):
func = _put_glance_image
elif FLAGS.image_service == "nova.image.s3.S3ImageService":
func = _put_s3_image
- elif FLAGS.image_service == "nova.image.local.LocalImageService":
- func = _put_local_image
else:
raise NotImplementedError(_("The Image Service %s is not implemented")
% FLAGS.image_service)
@@ -192,8 +188,6 @@ def get_vmdk_size_and_properties(image, instance):
size, properties = meta_data["size"], meta_data["properties"]
elif FLAGS.image_service == "nova.image.s3.S3ImageService":
raise NotImplementedError
- elif FLAGS.image_service == "nova.image.local.LocalImageService":
- raise NotImplementedError
LOG.debug(_("Got image size of %(size)s for the image %(image)s") %
locals())
return size, properties
diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py
index 113198689..d5ac39473 100644
--- a/nova/virt/xenapi/fake.py
+++ b/nova/virt/xenapi/fake.py
@@ -146,6 +146,7 @@ def create_vdi(name_label, read_only, sr_ref, sharable):
def create_vbd(vm_ref, vdi_ref):
vbd_rec = {'VM': vm_ref,
'VDI': vdi_ref,
+ 'userdevice': '0',
'currently_attached': False}
vbd_ref = _create_object('VBD', vbd_rec)
after_VBD_create(vbd_ref, vbd_rec)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 11da221f2..f91958c57 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -283,19 +283,16 @@ class VMHelper(HelperBase):
@classmethod
def get_vdi_for_vm_safely(cls, session, vm_ref):
- vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref)
- if vdi_refs is None:
- raise Exception(_("No VDIs found for VM %s") % vm_ref)
- else:
- num_vdis = len(vdi_refs)
- if num_vdis != 1:
- raise Exception(
- _("Unexpected number of VDIs (%(num_vdis)s) found"
- " for VM %(vm_ref)s") % locals())
-
- vdi_ref = vdi_refs[0]
- vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
- return vdi_ref, vdi_rec
+ """Retrieves the primary VDI for a VM"""
+ vbd_refs = session.get_xenapi().VM.get_VBDs(vm_ref)
+ for vbd in vbd_refs:
+ vbd_rec = session.get_xenapi().VBD.get_record(vbd)
+ # Convention dictates the primary VDI will be userdevice 0
+ if vbd_rec['userdevice'] == '0':
+ vdi_rec = session.get_xenapi().VDI.get_record(vbd_rec['VDI'])
+ return vbd_rec['VDI'], vdi_rec
+ raise exception.Error(_("No primary VDI found for"
+ "%(vm_ref)s") % locals())
@classmethod
def create_snapshot(cls, session, instance_id, vm_ref, label):
@@ -329,12 +326,6 @@ class VMHelper(HelperBase):
return template_vm_ref, template_vdi_uuids
@classmethod
- def get_sr(cls, session, sr_label='slices'):
- """Finds the SR named by the given name label and returns
- the UUID"""
- return session.call_xenapi('SR.get_by_name_label', sr_label)[0]
-
- @classmethod
def get_sr_path(cls, session):
"""Return the path to our storage repository
@@ -789,8 +780,7 @@ class VMHelper(HelperBase):
@classmethod
def scan_default_sr(cls, session):
"""Looks for the system default SR and triggers a re-scan"""
- #FIXME(sirp/mdietz): refactor scan_default_sr in there
- sr_ref = cls.get_sr(session)
+ sr_ref = find_sr(session)
session.call_xenapi('SR.scan', sr_ref)
@@ -882,7 +872,8 @@ def get_vdi_for_vm_safely(session, vm_ref):
else:
num_vdis = len(vdi_refs)
if num_vdis != 1:
- raise Exception(_("Unexpected number of VDIs (%(num_vdis)s) found"
+ raise exception.Exception(_("Unexpected number of VDIs"
+ "(%(num_vdis)s) found"
" for VM %(vm_ref)s") % locals())
vdi_ref = vdi_refs[0]