diff options
34 files changed, 512 insertions, 199 deletions
@@ -122,6 +122,7 @@ Todd Willey <todd@ansolabs.com> Trey Morris <trey.morris@rackspace.com> Troy Toman <troy.toman@rackspace.com> Tushar Patil <tushar.vitthal.patil@gmail.com> +Unmesh Gurjar <unmesh.gurjar@vertex.co.in> Vasiliy Shlykov <vash@vasiliyshlykov.org> Vishvananda Ishaya <vishvananda@gmail.com> Vivek Y S <vivek.ys@gmail.com> diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index a2bd2c32f..16ea74025 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -1091,17 +1091,19 @@ class CloudController(object): 'status': volume['attach_status'], 'volumeId': ec2utils.id_to_ec2_vol_id(volume_id)} - def _format_kernel_id(self, instance_ref, result, key): - kernel_id = instance_ref['kernel_id'] - if kernel_id is None: + def _format_kernel_id(self, context, instance_ref, result, key): + kernel_uuid = instance_ref['kernel_id'] + if kernel_uuid is None or kernel_uuid == '': return - result[key] = self.image_ec2_id(instance_ref['kernel_id'], 'aki') + kernel_id = self._get_image_id(context, kernel_uuid) + result[key] = self.image_ec2_id(kernel_id, 'aki') - def _format_ramdisk_id(self, instance_ref, result, key): - ramdisk_id = instance_ref['ramdisk_id'] - if ramdisk_id is None: + def _format_ramdisk_id(self, context, instance_ref, result, key): + ramdisk_uuid = instance_ref['ramdisk_id'] + if ramdisk_uuid is None or ramdisk_uuid == '': return - result[key] = self.image_ec2_id(instance_ref['ramdisk_id'], 'ari') + ramdisk_id = self._get_image_id(context, ramdisk_uuid) + result[key] = self.image_ec2_id(ramdisk_id, 'ari') @staticmethod def _format_user_data(instance_ref): @@ -1140,10 +1142,10 @@ class CloudController(object): self._format_instance_type(instance, result) def _format_attr_kernel(instance, result): - self._format_kernel_id(instance, result, 'kernel') + self._format_kernel_id(context, instance, result, 'kernel') def _format_attr_ramdisk(instance, result): - self._format_ramdisk_id(instance, result, 'ramdisk') + self._format_ramdisk_id(context, instance, result, 'ramdisk') def _format_attr_root_device_name(instance, result): self._format_instance_root_device_name(instance, result) @@ -1285,9 +1287,11 @@ class CloudController(object): instance_id = instance['id'] ec2_id = ec2utils.id_to_ec2_id(instance_id) i['instanceId'] = ec2_id - i['imageId'] = self.image_ec2_id(instance['image_ref']) - self._format_kernel_id(instance, i, 'kernelId') - self._format_ramdisk_id(instance, i, 'ramdiskId') + image_uuid = instance['image_ref'] + image_id = self._get_image_id(context, image_uuid) + i['imageId'] = self.image_ec2_id(image_id) + self._format_kernel_id(context, instance, i, 'kernelId') + self._format_ramdisk_id(context, instance, i, 'ramdiskId') i['instanceState'] = { 'code': instance['power_state'], 'name': state_description_from_vm_state(instance['vm_state'])} @@ -1399,14 +1403,16 @@ class CloudController(object): max_count = int(kwargs.get('max_count', 1)) if kwargs.get('kernel_id'): kernel = self._get_image(context, kwargs['kernel_id']) - kwargs['kernel_id'] = kernel['id'] + kwargs['kernel_id'] = self._get_image_uuid(context, kernel['id']) if kwargs.get('ramdisk_id'): ramdisk = self._get_image(context, kwargs['ramdisk_id']) - kwargs['ramdisk_id'] = ramdisk['id'] + kwargs['ramdisk_id'] = self._get_image_uuid(context, + ramdisk['id']) for bdm in kwargs.get('block_device_mapping', []): _parse_block_device_mapping(bdm) image = self._get_image(context, kwargs['image_id']) + image_uuid = self._get_image_uuid(context, image['id']) if image: image_state = self._get_image_state(image) @@ -1419,7 +1425,7 @@ class CloudController(object): (instances, resv_id) = self.compute_api.create(context, instance_type=instance_types.get_instance_type_by_name( kwargs.get('instance_type', None)), - image_href=self._get_image(context, kwargs['image_id'])['id'], + image_href=image_uuid, min_count=int(kwargs.get('min_count', max_count)), max_count=max_count, kernel_id=kwargs.get('kernel_id'), @@ -1516,7 +1522,7 @@ class CloudController(object): """Returns image ec2_id using id and three letter type.""" template = image_type + '-%08x' try: - return ec2utils.id_to_ec2_id(int(image_id), template=template) + return ec2utils.id_to_ec2_id(image_id, template=template) except ValueError: #TODO(wwolf): once we have ec2_id -> glance_id mapping # in place, this wont be necessary @@ -1536,6 +1542,15 @@ class CloudController(object): raise exception.ImageNotFound(image_id=ec2_id) return image + # NOTE(bcwaldon): We need access to the image uuid since we directly + # call the compute api from this class + def _get_image_uuid(self, context, internal_id): + return self.image_service.get_image_uuid(context, internal_id) + + # NOTE(bcwaldon): We also need to be able to map image uuids to integers + def _get_image_id(self, context, image_uuid): + return self.image_service.get_image_id(context, image_uuid) + def _format_image(self, image): """Convert from format defined by GlanceImageService to S3 format.""" i = {} diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py index aac8f9a1e..514dabe48 100644 --- a/nova/api/ec2/ec2utils.py +++ b/nova/api/ec2/ec2utils.py @@ -31,7 +31,7 @@ def ec2_id_to_id(ec2_id): def id_to_ec2_id(instance_id, template='i-%08x'): """Convert an instance ID (int) to an ec2 ID (i-[base 16 number])""" - return template % instance_id + return template % int(instance_id) def id_to_ec2_snap_id(instance_id): diff --git a/nova/compute/manager.py b/nova/compute/manager.py index f133ee133..b1e75cd9a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -354,12 +354,12 @@ class ComputeManager(manager.SchedulerDependentManager): allowed_size_bytes = allowed_size_gb * 1024 * 1024 * 1024 - LOG.debug(_("image_id=%(image_id)d, image_size_bytes=" + LOG.debug(_("image_id=%(image_id)s, image_size_bytes=" "%(size_bytes)d, allowed_size_bytes=" "%(allowed_size_bytes)d") % locals()) if size_bytes > allowed_size_bytes: - LOG.info(_("Image '%(image_id)d' size %(size_bytes)d exceeded" + LOG.info(_("Image '%(image_id)s' size %(size_bytes)d exceeded" " instance_type allowed size " "%(allowed_size_bytes)d") % locals()) @@ -798,7 +798,7 @@ class ComputeManager(manager.SchedulerDependentManager): for i in xrange(excess): image = images.pop() image_id = image['id'] - LOG.debug(_("Deleting image %d" % image_id)) + LOG.debug(_("Deleting image %s" % image_id)) image_service.delete(context, image_id) @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) diff --git a/nova/db/api.py b/nova/db/api.py index 915589fb3..c5d47281d 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1593,3 +1593,21 @@ def vsa_get_all(context): def vsa_get_all_by_project(context, project_id): """Get all Virtual Storage Array records by project ID.""" return IMPL.vsa_get_all_by_project(context, project_id) + + +################### + + +def s3_image_get(context, image_id): + """Find local s3 image represented by the provided id""" + return IMPL.s3_image_get(context, image_id) + + +def s3_image_get_by_uuid(context, image_uuid): + """Find local s3 image represented by the provided uuid""" + return IMPL.s3_image_get_by_uuid(context, image_uuid) + + +def s3_image_create(context, image_uuid): + """Create local s3 image represented by provided uuid""" + return IMPL.s3_image_create(context, image_uuid) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 47efb9019..eb3d6105c 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1792,6 +1792,7 @@ def network_count_reserved_ips(context, network_id): @require_admin_context def network_create_safe(context, values): network_ref = models.Network() + network_ref['uuid'] = str(utils.gen_uuid()) network_ref.update(values) try: network_ref.save() @@ -4017,4 +4018,42 @@ def vsa_get_all_by_project(context, project_id): all() - #################### +#################### + + +def s3_image_get(context, image_id): + """Find local s3 image represented by the provided id""" + session = get_session() + res = session.query(models.S3Image)\ + .filter_by(id=image_id)\ + .first() + + if not res: + raise exception.ImageNotFound(image_id=image_id) + + return res + + +def s3_image_get_by_uuid(context, image_uuid): + """Find local s3 image represented by the provided uuid""" + session = get_session() + res = session.query(models.S3Image)\ + .filter_by(uuid=image_uuid)\ + .first() + + if not res: + raise exception.ImageNotFound(image_id=image_uuid) + + return res + + +def s3_image_create(context, image_uuid): + """Create local s3 image represented by provided uuid""" + try: + s3_image_ref = models.S3Image() + s3_image_ref.update({'uuid': image_uuid}) + s3_image_ref.save() + except Exception, e: + raise exception.DBError(e) + + return s3_image_ref diff --git a/nova/db/sqlalchemy/migrate_repo/versions/056_add_s3_images.py b/nova/db/sqlalchemy/migrate_repo/versions/056_add_s3_images.py new file mode 100644 index 000000000..bcd00be5b --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/056_add_s3_images.py @@ -0,0 +1,57 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +import sqlalchemy + +from nova import log as logging + + +meta = sqlalchemy.MetaData() + + +s3_images = sqlalchemy.Table('s3_images', meta, + sqlalchemy.Column('created_at', + sqlalchemy.DateTime(timezone=False)), + sqlalchemy.Column('updated_at', + sqlalchemy.DateTime(timezone=False)), + sqlalchemy.Column('deleted_at', + sqlalchemy.DateTime(timezone=False)), + sqlalchemy.Column('deleted', + sqlalchemy.Boolean(create_constraint=True, name=None)), + sqlalchemy.Column('id', sqlalchemy.Integer(), + primary_key=True, + nullable=False, + autoincrement=True), + sqlalchemy.Column('uuid', sqlalchemy.String(36), + nullable=False)) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + + try: + s3_images.create() + except Exception: + logging.exception("Exception while creating table 's3_images'") + meta.drop_all(tables=[s3_images]) + raise + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + + s3_images.drop() diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index e493c78d5..56f2b4aad 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -873,6 +873,13 @@ class BandwidthUsage(BASE, NovaBase): bw_out = Column(BigInteger) +class S3Image(BASE, NovaBase): + """Compatibility layer for the S3 image service talking to Glance""" + __tablename__ = 's3_images' + id = Column(Integer, primary_key=True, nullable=False, autoincrement=True) + uuid = Column(String(36), nullable=False) + + def register_models(): """Register Models and create metadata. diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py index e62aa99f8..9491720e3 100644 --- a/nova/db/sqlalchemy/session.py +++ b/nova/db/sqlalchemy/session.py @@ -19,6 +19,7 @@ """Session Handling for SQLAlchemy backend.""" import sqlalchemy.exc +import sqlalchemy.interfaces import sqlalchemy.orm import time @@ -48,6 +49,13 @@ def get_session(autocommit=True, expire_on_commit=False): return session +class SynchronousSwitchListener(sqlalchemy.interfaces.PoolListener): + """Switch sqlite connections to non-synchronous mode""" + + def connect(self, dbapi_con, con_record): + dbapi_con.execute("PRAGMA synchronous = OFF") + + def get_engine(): """Return a SQLAlchemy engine.""" connection_dict = sqlalchemy.engine.url.make_url(FLAGS.sql_connection) @@ -59,6 +67,8 @@ def get_engine(): if "sqlite" in connection_dict.drivername: engine_args["poolclass"] = sqlalchemy.pool.NullPool + if not FLAGS.sqlite_synchronous: + engine_args["listeners"] = [SynchronousSwitchListener()] engine = sqlalchemy.create_engine(FLAGS.sql_connection, **engine_args) ensure_connection(engine) diff --git a/nova/flags.py b/nova/flags.py index 7253ad553..20225eba5 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -315,7 +315,7 @@ DEFINE_string('ajax_console_proxy_url', 'http://127.0.0.1:8000', 'location of ajax console proxy, \ in the form "http://127.0.0.1:8000"') -DEFINE_string('ajax_console_proxy_port', +DEFINE_integer('ajax_console_proxy_port', 8000, 'port that ajax_console_proxy binds') DEFINE_string('vsa_topic', 'vsa', 'the topic that nova-vsa service listens on') DEFINE_bool('verbose', False, 'show debug output') @@ -362,7 +362,7 @@ DEFINE_string('null_kernel', 'nokernel', 'kernel image that indicates not to use a kernel,' ' but to use a raw disk image instead') -DEFINE_integer('vpn_image_id', 0, 'integer id for cloudpipe vpn server') +DEFINE_string('vpn_image_id', '0', 'image id for cloudpipe vpn server') DEFINE_string('vpn_key_suffix', '-vpn', 'Suffix to add to project name for vpn key and secgroups') @@ -377,6 +377,7 @@ DEFINE_string('logdir', None, 'output to a per-service log file in named ' 'directory') DEFINE_string('logfile_mode', '0644', 'Default file mode of the logs.') DEFINE_string('sqlite_db', 'nova.sqlite', 'file name for sqlite') +DEFINE_bool('sqlite_synchronous', True, 'Synchronous mode for sqlite') DEFINE_string('sql_connection', 'sqlite:///$state_path/$sqlite_db', 'connection string for sql database') diff --git a/nova/image/__init__.py b/nova/image/__init__.py index 307b73f01..c5ca6f3e6 100644 --- a/nova/image/__init__.py +++ b/nova/image/__init__.py @@ -40,10 +40,12 @@ def get_image_service(context, image_href): :returns: a tuple of the form (image_service, image_id) """ - image_href = image_href or 0 - if str(image_href).isdigit(): - return (get_default_image_service(), int(image_href)) - - (glance_client, image_id) = glance.get_glance_client(context, image_href) - image_service = nova.image.glance.GlanceImageService(glance_client) - return (image_service, image_id) + # check if this is not a uri + if '/' not in str(image_href): + return (get_default_image_service(), image_href) + + else: + (glance_client, image_id) = glance.get_glance_client(context, + image_href) + image_service = nova.image.glance.GlanceImageService(glance_client) + return (image_service, image_id) diff --git a/nova/image/fake.py b/nova/image/fake.py index f5b967a8f..a51a8ecb4 100644 --- a/nova/image/fake.py +++ b/nova/image/fake.py @@ -24,6 +24,7 @@ import random from nova import exception from nova import flags from nova import log as logging +from nova import utils LOG = logging.getLogger('nova.image.fake') @@ -40,7 +41,9 @@ class _FakeImageService(object): # NOTE(justinsb): The OpenStack API can't upload an image? # So, make sure we've got one.. timestamp = datetime.datetime(2011, 01, 01, 01, 02, 03) - image1 = {'id': '123456', + + # NOTE(bcwaldon): was image '123456' + image1 = {'id': '155d900f-4e14-4e4c-a73d-069cbf4541e6', 'name': 'fakeimage123456', 'created_at': timestamp, 'updated_at': timestamp, @@ -54,7 +57,8 @@ class _FakeImageService(object): 'ramdisk_id': FLAGS.null_kernel, 'architecture': 'x86_64'}} - image2 = {'id': 'fake', + # NOTE(bcwaldon): was image 'fake' + image2 = {'id': 'a2459075-d96c-40d5-893e-577ff92e721c', 'name': 'fakeimage123456', 'created_at': timestamp, 'updated_at': timestamp, @@ -67,7 +71,8 @@ class _FakeImageService(object): 'properties': {'kernel_id': FLAGS.null_kernel, 'ramdisk_id': FLAGS.null_kernel}} - image3 = {'id': '2', + # NOTE(bcwaldon): was image '2' + image3 = {'id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', 'name': 'fakeimage123456', 'created_at': timestamp, 'updated_at': timestamp, @@ -80,7 +85,8 @@ class _FakeImageService(object): 'properties': {'kernel_id': FLAGS.null_kernel, 'ramdisk_id': FLAGS.null_kernel}} - image4 = {'id': '1', + # NOTE(bcwaldon): was image '1' + image4 = {'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'name': 'fakeimage123456', 'created_at': timestamp, 'updated_at': timestamp, @@ -93,7 +99,8 @@ class _FakeImageService(object): 'properties': {'kernel_id': FLAGS.null_kernel, 'ramdisk_id': FLAGS.null_kernel}} - image5 = {'id': '3', + # NOTE(bcwaldon): was image '3' + image5 = {'id': 'c905cedb-7281-47e4-8a62-f26bc5fc4c77', 'name': 'fakeimage123456', 'created_at': timestamp, 'updated_at': timestamp, @@ -152,20 +159,10 @@ class _FakeImageService(object): :raises: Duplicate if the image already exist. """ - try: - image_id = metadata['id'] - except KeyError: - while True: - image_id = random.randint(0, 2 ** 31 - 1) - if not self.images.get(str(image_id)): - break - - image_id = str(image_id) - - if self.images.get(image_id): - raise exception.Duplicate() - + image_id = str(metadata.get('id', utils.gen_uuid())) metadata['id'] = image_id + if image_id in self.images: + raise exception.Duplicate() self.images[image_id] = copy.deepcopy(metadata) return self.images[image_id] diff --git a/nova/image/glance.py b/nova/image/glance.py index d9905297a..c9ce946b8 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -54,7 +54,7 @@ def _parse_image_ref(image_href): o = urlparse(image_href) port = o.port or 80 host = o.netloc.split(':', 1)[0] - image_id = int(o.path.split('/')[-1]) + image_id = o.path.split('/')[-1] return (image_id, host, port) @@ -99,20 +99,25 @@ def get_glance_client(context, image_href): :returns: a tuple of the form (glance_client, image_id) """ - image_href = image_href or 0 glance_host, glance_port = pick_glance_api_server() - if str(image_href).isdigit(): - glance_client = _create_glance_client(context, glance_host, + # check if this is an id + if '/' not in str(image_href): + glance_client = _create_glance_client(context, + glance_host, glance_port) - return (glance_client, int(image_href)) - - try: - (image_id, host, port) = _parse_image_ref(image_href) - except ValueError: - raise exception.InvalidImageRef(image_href=image_href) - glance_client = _create_glance_client(context, glance_host, glance_port) - return (glance_client, image_id) + return (glance_client, image_href) + + else: + try: + (image_id, host, port) = _parse_image_ref(image_href) + except ValueError: + raise exception.InvalidImageRef(image_href=image_href) + + glance_client = _create_glance_client(context, + glance_host, + glance_port) + return (glance_client, image_id) class GlanceImageService(object): diff --git a/nova/image/s3.py b/nova/image/s3.py index 343555887..6f07b5f72 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -29,6 +29,7 @@ import boto.s3.connection import eventlet from nova import crypto +import nova.db.api from nova import exception from nova import flags from nova import image @@ -54,6 +55,44 @@ class S3ImageService(object): self.service = service or image.get_default_image_service() self.service.__init__(*args, **kwargs) + def get_image_uuid(self, context, image_id): + return nova.db.api.s3_image_get(context, image_id)['uuid'] + + def get_image_id(self, context, image_uuid): + return nova.db.api.s3_image_get_by_uuid(context, image_uuid)['id'] + + def _create_image_id(self, context, image_uuid): + return nova.db.api.s3_image_create(context, image_uuid)['id'] + + def _translate_uuids_to_ids(self, context, images): + return [self._translate_uuid_to_id(context, img) for img in images] + + def _translate_uuid_to_id(self, context, image): + def _find_or_create(image_uuid): + try: + return self.get_image_id(context, image_uuid) + except exception.NotFound: + return self._create_image_id(context, image_uuid) + + image_copy = image.copy() + + try: + image_id = image_copy['id'] + except KeyError: + pass + else: + image_copy['id'] = _find_or_create(image_copy['id']) + + for prop in ['kernel_id', 'ramdisk_id']: + try: + image_uuid = image_copy['properties'][prop] + except (KeyError, ValueError): + pass + else: + image_copy['properties'][prop] = _find_or_create(image_uuid) + + return image_copy + def create(self, context, metadata, data=None): """Create an image. @@ -64,23 +103,38 @@ class S3ImageService(object): return image def delete(self, context, image_id): - self.service.delete(context, image_id) + image_uuid = self.get_image_uuid(context, image_id) + self.service.delete(context, image_uuid) def update(self, context, image_id, metadata, data=None): - image = self.service.update(context, image_id, metadata, data) - return image + image_uuid = self.get_image_uuid(context, image_id) + image = self.service.update(context, image_uuid, metadata, data) + return self._translate_uuid_to_id(context, image) def index(self, context): - return self.service.index(context) + #NOTE(bcwaldon): sort asc to make sure we assign lower ids + # to older images + images = self.service.index(context, sort_dir='asc') + return self._translate_uuids_to_ids(context, images) def detail(self, context): - return self.service.detail(context) + #NOTE(bcwaldon): sort asc to make sure we assign lower ids + # to older images + images = self.service.detail(context, sort_dir='asc') + return self._translate_uuids_to_ids(context, images) def show(self, context, image_id): - return self.service.show(context, image_id) + image_uuid = self.get_image_uuid(context, image_id) + image = self.service.show(context, image_uuid) + return self._translate_uuid_to_id(context, image) def show_by_name(self, context, name): - return self.service.show_by_name(context, name) + image = self.service.show_by_name(context, name) + return self._translate_uuid_to_id(context, image) + + def get(self, context, image_id): + image_uuid = self.get_image_uuid(context, image_id) + return self.get(self, context, image_uuid) @staticmethod def _conn(context): @@ -158,10 +212,14 @@ class S3ImageService(object): properties['architecture'] = arch if kernel_id: - properties['kernel_id'] = ec2utils.ec2_id_to_id(kernel_id) + kernel_id = ec2_utils.ec2_id_to_id(kernel_id) + kernel_uuid = self._get_image_uuid(context, kernel_id) + properties['kernel_id'] = kernel_uuid if ramdisk_id: - properties['ramdisk_id'] = ec2utils.ec2_id_to_id(ramdisk_id) + ramdisk_id = ec2utils.ec2_id_to_id(ramdisk_id) + ramdisk_uuid = self._get_image_uuid(context, ramdisk_id) + properties['ramdisk_id'] = ramdisk_uuid if mappings: properties['mappings'] = mappings @@ -172,8 +230,19 @@ class S3ImageService(object): 'is_public': False, 'properties': properties}) metadata['properties']['image_state'] = 'pending' + + #TODO(bcwaldon): right now, this removes user-defined ids. + # We need to re-enable this. + image_id = metadata.pop('id', None) + image = self.service.create(context, metadata) - return manifest, image + + # extract the new uuid and generate an int id to present back to user + image_uuid = image['id'] + image['id'] = self._create_image_id(context, image_uuid) + + # return image_uuid so the caller can still make use of image_service + return manifest, image, image_uuid def _s3_create(self, context, metadata): """Gets a manifext from s3 and makes an image.""" @@ -187,15 +256,16 @@ class S3ImageService(object): key = bucket.get_key(manifest_path) manifest = key.get_contents_as_string() - manifest, image = self._s3_parse_manifest(context, metadata, manifest) - image_id = image['id'] + manifest, image, image_uuid = self._s3_parse_manifest(context, + metadata, + manifest) def delayed_create(): """This handles the fetching and decrypting of the part files.""" log_vars = {'image_location': image_location, 'image_path': image_path} metadata['properties']['image_state'] = 'downloading' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) try: parts = [] @@ -217,11 +287,11 @@ class S3ImageService(object): LOG.exception(_("Failed to download %(image_location)s " "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_download' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) return metadata['properties']['image_state'] = 'decrypting' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) try: hex_key = manifest.find('image/ec2_encrypted_key').text @@ -241,11 +311,11 @@ class S3ImageService(object): LOG.exception(_("Failed to decrypt %(image_location)s " "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_decrypt' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) return metadata['properties']['image_state'] = 'untarring' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) try: unz_filename = self._untarzip_image(image_path, dec_filename) @@ -253,25 +323,25 @@ class S3ImageService(object): LOG.exception(_("Failed to untar %(image_location)s " "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_untar' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) return metadata['properties']['image_state'] = 'uploading' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) try: with open(unz_filename) as image_file: - self.service.update(context, image_id, + self.service.update(context, image_uuid, metadata, image_file) except Exception: LOG.exception(_("Failed to upload %(image_location)s " "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_upload' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) return metadata['properties']['image_state'] = 'available' metadata['status'] = 'active' - self.service.update(context, image_id, metadata) + self.service.update(context, image_uuid, metadata) shutil.rmtree(image_path) diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 23a9aba0d..404578474 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -241,7 +241,9 @@ class QuantumManager(manager.FlatManager): 'injected': True, 'multi_host': False} + q_tenant_id = project_id or FLAGS.quantum_default_tenant_id info = { + 'label': self.q_conn.get_network_name(q_tenant_id, net_id), 'gateway': v4_subnet['gateway'], 'dhcp_server': v4_subnet['gateway'], 'broadcast': v4_subnet['broadcast'], diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index 21917653c..ce07bc1ab 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -60,6 +60,10 @@ class QuantumClientConnection(object): resdict = self.client.create_network(data, tenant=tenant_id) return resdict["network"]["id"] + def get_network_name(self, tenant_id, network_id): + net = self.client.show_network_details(network_id, tenant=tenant_id) + return net["network"]["name"] + def delete_network(self, tenant_id, net_id): """Deletes Quantum network with specified UUID.""" self.client.delete_network(net_id, tenant=tenant_id) diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index 772f4dafe..c0f3d44d7 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -17,6 +17,7 @@ # under the License. import base64 +import copy import functools import os @@ -110,9 +111,13 @@ class CloudTestCase(test.TestCase): True) def fake_show(meh, context, id): - return {'id': 1, 'container_format': 'ami', - 'properties': {'kernel_id': 1, 'ramdisk_id': 1, - 'type': 'machine', 'image_state': 'available'}} + return {'id': id, + 'container_format': 'ami', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'type': 'machine', + 'image_state': 'available'}} self.stubs.Set(fake._FakeImageService, 'show', fake_show) self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show) @@ -121,6 +126,12 @@ class CloudTestCase(test.TestCase): # ensure that operations complete self.stubs.Set(rpc, 'cast', rpc.call) + # make sure we can map ami-00000001/2 to a uuid in FakeImageService + db.api.s3_image_create(self.context, + 'cedef40a-ed67-4d10-800e-17455edce175') + db.api.s3_image_create(self.context, + '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6') + def _stub_instance_get_with_fixed_ips(self, func_name): orig_func = getattr(self.cloud.compute_api, func_name) @@ -540,13 +551,14 @@ class CloudTestCase(test.TestCase): self._stub_instance_get_with_fixed_ips('get_all') self._stub_instance_get_with_fixed_ips('get') + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' inst1 = db.instance_create(self.context, {'reservation_id': 'a', - 'image_ref': 1, + 'image_ref': image_uuid, 'instance_type_id': 1, 'host': 'host1', 'vm_state': 'active'}) inst2 = db.instance_create(self.context, {'reservation_id': 'a', - 'image_ref': 1, + 'image_ref': image_uuid, 'instance_type_id': 1, 'host': 'host2', 'vm_state': 'active'}) @@ -589,8 +601,9 @@ class CloudTestCase(test.TestCase): self._stub_instance_get_with_fixed_ips('get_all') self._stub_instance_get_with_fixed_ips('get') + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' inst1 = db.instance_create(self.context, {'reservation_id': 'a', - 'image_ref': 1, + 'image_ref': image_uuid, 'instance_type_id': 1, 'vm_state': 'active'}) comp1 = db.service_create(self.context, {'host': 'host1', @@ -611,14 +624,15 @@ class CloudTestCase(test.TestCase): db.service_destroy(self.context, comp1['id']) def test_describe_instances_deleted(self): + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' args1 = {'reservation_id': 'a', - 'image_ref': 1, + 'image_ref': image_uuid, 'instance_type_id': 1, 'host': 'host1', 'vm_state': 'active'} inst1 = db.instance_create(self.context, args1) args2 = {'reservation_id': 'b', - 'image_ref': 1, + 'image_ref': image_uuid, 'instance_type_id': 1, 'host': 'host1', 'vm_state': 'active'} @@ -649,12 +663,13 @@ class CloudTestCase(test.TestCase): return volumes def _setUpBlockDeviceMapping(self): + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' inst1 = db.instance_create(self.context, - {'image_ref': 1, + {'image_ref': image_uuid, 'instance_type_id': 1, 'root_device_name': '/dev/sdb1'}) inst2 = db.instance_create(self.context, - {'image_ref': 2, + {'image_ref': image_uuid, 'instance_type_id': 1, 'root_device_name': '/dev/sdc1'}) @@ -814,9 +829,12 @@ class CloudTestCase(test.TestCase): def test_describe_images(self): describe_images = self.cloud.describe_images - def fake_detail(meh, context): - return [{'id': 1, 'container_format': 'ami', - 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + def fake_detail(meh, context, **kwargs): + return [{'id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'container_format': 'ami', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'type': 'machine'}}] def fake_show_none(meh, context, id): @@ -876,9 +894,9 @@ class CloudTestCase(test.TestCase): {'device_name': '/dev/sdc3', 'virtual_name': 'ephemeral6'}, {'device_name': '/dev/sdc4', 'no_device': True}] image1 = { - 'id': 1, + 'id': 'cedef40a-ed67-4d10-800e-17455edce175', 'properties': { - 'kernel_id': 1, + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'type': 'machine', 'image_state': 'available', 'mappings': mappings1, @@ -890,22 +908,23 @@ class CloudTestCase(test.TestCase): block_device_mapping2 = [{'device_name': '/dev/sdb1', 'snapshot_id': 01234567}] image2 = { - 'id': 2, + 'id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', 'properties': { - 'kernel_id': 2, + 'kernel_id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', 'type': 'machine', 'root_device_name': '/dev/sdb1', 'mappings': mappings2, 'block_device_mapping': block_device_mapping2}} def fake_show(meh, context, image_id): - for i in [image1, image2]: - if i['id'] == image_id: + _images = [copy.deepcopy(image1), copy.deepcopy(image2)] + for i in _images: + if str(i['id']) == str(image_id): return i raise exception.ImageNotFound(image_id=image_id) def fake_detail(meh, context): - return [image1, image2] + return [copy.deepcopy(image1), copy.deepcopy(image2)] self.stubs.Set(fake._FakeImageService, 'show', fake_show) self.stubs.Set(fake._FakeImageService, 'detail', fake_detail) @@ -1000,8 +1019,12 @@ class CloudTestCase(test.TestCase): describe_image_attribute = self.cloud.describe_image_attribute def fake_show(meh, context, id): - return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, - 'type': 'machine'}, 'container_format': 'ami', + return {'id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'type': 'machine'}, + 'container_format': 'ami', 'is_public': True} self.stubs.Set(fake._FakeImageService, 'show', fake_show) @@ -1039,9 +1062,14 @@ class CloudTestCase(test.TestCase): def test_modify_image_attribute(self): modify_image_attribute = self.cloud.modify_image_attribute - fake_metadata = {'id': 1, 'container_format': 'ami', - 'properties': {'kernel_id': 1, 'ramdisk_id': 1, - 'type': 'machine'}, 'is_public': False} + fake_metadata = { + 'id': 1, + 'container_format': 'ami', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'type': 'machine'}, + 'is_public': False} def fake_show(meh, context, id): return fake_metadata @@ -1173,10 +1201,22 @@ class CloudTestCase(test.TestCase): self.cloud.delete_key_pair(self.context, 'test') def test_run_instances(self): - kwargs = {'image_id': FLAGS.default_image, + kwargs = {'image_id': 'ami-00000001', 'instance_type': FLAGS.default_instance_type, 'max_count': 1} run_instances = self.cloud.run_instances + + def fake_show(self, context, id): + return {'id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'type': 'machine'}, + 'container_format': 'ami', + 'status': 'active'} + + self.stubs.UnsetAll() + self.stubs.Set(fake._FakeImageService, 'show', fake_show) + result = run_instances(self.context, **kwargs) instance = result['instancesSet'][0] self.assertEqual(instance['imageId'], 'ami-00000001') @@ -1186,13 +1226,16 @@ class CloudTestCase(test.TestCase): self.assertEqual(instance['instanceType'], 'm1.small') def test_run_instances_image_state_none(self): - kwargs = {'image_id': FLAGS.default_image, + kwargs = {'image_id': 'ami-00000001', 'instance_type': FLAGS.default_instance_type, 'max_count': 1} run_instances = self.cloud.run_instances def fake_show_no_state(self, context, id): - return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + return {'id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'type': 'machine'}, 'container_format': 'ami'} self.stubs.UnsetAll() @@ -1201,14 +1244,17 @@ class CloudTestCase(test.TestCase): self.context, **kwargs) def test_run_instances_image_state_invalid(self): - kwargs = {'image_id': FLAGS.default_image, + kwargs = {'image_id': 'ami-00000001', 'instance_type': FLAGS.default_instance_type, 'max_count': 1} run_instances = self.cloud.run_instances def fake_show_decrypt(self, context, id): - return {'id': 1, 'container_format': 'ami', - 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + return {'id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'container_format': 'ami', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', 'type': 'machine', 'image_state': 'decrypting'}} self.stubs.UnsetAll() @@ -1223,9 +1269,13 @@ class CloudTestCase(test.TestCase): run_instances = self.cloud.run_instances def fake_show_stat_active(self, context, id): - return {'id': 1, 'container_format': 'ami', - 'properties': {'kernel_id': 1, 'ramdisk_id': 1, - 'type': 'machine'}, 'status': 'active'} + return {'id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'container_format': 'ami', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'type': 'machine'}, + 'status': 'active'} self.stubs.Set(fake._FakeImageService, 'show', fake_show_stat_active) @@ -1648,8 +1698,8 @@ class CloudTestCase(test.TestCase): 'security_groups': [{'name': 'fake0'}, {'name': 'fake1'}], 'vm_state': vm_states.STOPPED, 'instance_type': {'name': 'fake_type'}, - 'kernel_id': 1, - 'ramdisk_id': 2, + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', 'user_data': 'fake-user data', } self.stubs.Set(self.cloud.compute_api, 'get', fake_get) diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index 7bc830436..fe1a7a94f 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -138,10 +138,11 @@ class CreateserverextTest(test.TestCase): compute_api = MockComputeAPI() self.stubs.Set(nova.compute, 'API', self._make_stub_method(compute_api)) + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' self.stubs.Set( nova.api.openstack.servers.Controller, '_get_kernel_ramdisk_from_image', - self._make_stub_method((1, 1))) + self._make_stub_method((image_uuid, image_uuid))) return compute_api def _setup_mock_network_api(self): @@ -150,7 +151,7 @@ class CreateserverextTest(test.TestCase): def _create_security_group_request_dict(self, security_groups): server = {} server['name'] = 'new-server-test' - server['imageRef'] = 1 + server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' server['flavorRef'] = 1 if security_groups is not None: sg_list = [] @@ -162,7 +163,7 @@ class CreateserverextTest(test.TestCase): def _create_networks_request_dict(self, networks): server = {} server['name'] = 'new-server-test' - server['imageRef'] = 1 + server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' server['flavorRef'] = 1 if networks is not None: network_list = [] @@ -174,7 +175,7 @@ class CreateserverextTest(test.TestCase): def _create_user_data_request_dict(self, user_data): server = {} server['name'] = 'new-server-test' - server['imageRef'] = 1 + server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' server['flavorRef'] = 1 server['user_data'] = user_data return {'server': server} diff --git a/nova/tests/api/openstack/contrib/test_volumes.py b/nova/tests/api/openstack/contrib/test_volumes.py index 419e8be0d..0a3023e48 100644 --- a/nova/tests/api/openstack/contrib/test_volumes.py +++ b/nova/tests/api/openstack/contrib/test_volumes.py @@ -28,6 +28,7 @@ FLAGS = flags.FLAGS FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' +IMAGE_UUID = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77' def fake_compute_api_create(cls, context, instance_type, image_href, **kwargs): @@ -42,7 +43,7 @@ def fake_compute_api_create(cls, context, instance_type, image_href, **kwargs): 'instance_type': dict(inst_type), 'access_ip_v4': '1.2.3.4', 'access_ip_v6': 'fead::1234', - 'image_ref': 3, + 'image_ref': IMAGE_UUID, 'user_id': 'fake', 'project_id': 'fake', 'created_at': datetime.datetime(2010, 10, 10, 12, 0, 0), @@ -61,7 +62,7 @@ class BootFromVolumeTest(test.TestCase): def test_create_root_volume(self): body = dict(server=dict( - name='test_server', imageRef=3, + name='test_server', imageRef=IMAGE_UUID, flavorRef=2, min_count=1, max_count=1, block_device_mapping=[dict( volume_id=1, @@ -82,7 +83,7 @@ class BootFromVolumeTest(test.TestCase): self.assertEqual(FAKE_UUID, server['id']) self.assertEqual(2, int(server['flavor']['id'])) self.assertEqual(u'test_server', server['name']) - self.assertEqual(3, int(server['image']['id'])) + self.assertEqual(IMAGE_UUID, server['image']['id']) self.assertEqual(FLAGS.password_length, len(server['adminPass'])) self.assertEqual(len(_block_device_mapping_seen), 1) self.assertEqual(_block_device_mapping_seen[0]['volume_id'], 1) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 29c0f2fcc..e6e528535 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1349,7 +1349,8 @@ class ServersControllerCreateTest(test.TestCase): def instance_create(context, inst): inst_type = instance_types.get_instance_type_by_flavor_id(3) - image_ref = 'http://localhost/images/2' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + def_image_ref = 'http://localhost/images/%s' % image_uuid self.instance_cache_num += 1 instance = { 'id': self.instance_cache_num, @@ -1358,7 +1359,7 @@ class ServersControllerCreateTest(test.TestCase): 'instance_type': dict(inst_type), 'access_ip_v4': '1.2.3.4', 'access_ip_v6': 'fead::1234', - 'image_ref': image_ref, + 'image_ref': inst.get('image_ref', def_image_ref), 'user_id': 'fake', 'project_id': 'fake', 'reservation_id': inst['reservation_id'], @@ -1402,10 +1403,8 @@ class ServersControllerCreateTest(test.TestCase): return 'network_topic' def kernel_ramdisk_mapping(*args, **kwargs): - return (1, 1) - - def image_id_from_hash(*args, **kwargs): - return 2 + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' + return (image_uuid, image_uuid) fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) @@ -1430,8 +1429,9 @@ class ServersControllerCreateTest(test.TestCase): self.stubs.Set(nova.compute.api.API, "_find_host", find_host) def _test_create_instance(self): + image_uuid = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77' body = dict(server=dict( - name='server_test', imageRef=3, flavorRef=2, + name='server_test', imageRef=image_uuid, flavorRef=2, metadata={'hello': 'world', 'open': 'stack'}, personality={})) req = fakes.HTTPRequest.blank('/v1.1/fake/servers') @@ -1444,13 +1444,13 @@ class ServersControllerCreateTest(test.TestCase): self.assertEqual('server_test', server['name']) self.assertEqual(FAKE_UUID, server['id']) self.assertEqual('2', server['flavor']['id']) - self.assertEqual('3', server['image']['id']) + self.assertEqual(image_uuid, server['image']['id']) def test_create_multiple_instances(self): """Test creating multiple instances but not asking for reservation_id """ - image_href = 'http://localhost/v1.1/123/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/123/flavors/3' body = { 'server': { @@ -1476,7 +1476,7 @@ class ServersControllerCreateTest(test.TestCase): """Test creating multiple instances with asking for reservation_id """ - image_href = 'http://localhost/v1.1/123/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/123/flavors/3' body = { 'server': { @@ -1504,7 +1504,7 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_with_user_supplied_reservation_id(self): """Non-admin supplied reservation_id should be ignored.""" - image_href = 'http://localhost/v1.1/123/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/123/flavors/3' body = { 'server': { @@ -1530,7 +1530,7 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_with_admin_supplied_reservation_id(self): """Admin supplied reservation_id should be honored.""" - image_href = 'http://localhost/v1.1/123/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/123/flavors/3' body = { 'server': { @@ -1561,7 +1561,8 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_with_access_ip(self): # proper local hrefs must start with 'http://localhost/v1.1/' - image_href = 'http://localhost/v1.1/fake/images/2' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v1.1/fake/images/%s' % image_uuid flavor_ref = 'http://localhost/fake/flavors/3' access_ipv4 = '1.2.3.4' access_ipv6 = 'fead::1234' @@ -1575,11 +1576,11 @@ class ServersControllerCreateTest(test.TestCase): ], } expected_image = { - "id": "2", + "id": image_uuid, "links": [ { "rel": "bookmark", - "href": 'http://localhost/fake/images/2', + "href": 'http://localhost/fake/images/%s' % image_uuid, }, ], } @@ -1621,7 +1622,8 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance(self): # proper local hrefs must start with 'http://localhost/v1.1/' - image_href = 'http://localhost/v1.1/images/2' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v1.1/images/%s' % image_uuid flavor_ref = 'http://localhost/123/flavors/3' expected_flavor = { "id": "3", @@ -1633,11 +1635,11 @@ class ServersControllerCreateTest(test.TestCase): ], } expected_image = { - "id": "2", + "id": image_uuid, "links": [ { "rel": "bookmark", - "href": 'http://localhost/fake/images/2', + "href": 'http://localhost/fake/images/%s' % image_uuid, }, ], } @@ -1691,7 +1693,7 @@ class ServersControllerCreateTest(test.TestCase): self.controller.create, req, body) def test_create_instance_valid_key_name(self): - image_href = 'http://localhost/v1.1/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/flavors/3' body = dict(server=dict( name='server_test', imageRef=image_href, flavorRef=flavor_ref, @@ -1751,7 +1753,7 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_with_config_drive(self): self.config_drive = True - image_href = 'http://localhost/v1.1/123/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { @@ -1779,7 +1781,7 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_with_config_drive_as_id(self): self.config_drive = 2 - image_href = 'http://localhost/v1.1/123/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { @@ -1791,7 +1793,7 @@ class ServersControllerCreateTest(test.TestCase): 'open': 'stack', }, 'personality': {}, - 'config_drive': 2, + 'config_drive': image_href, }, } @@ -1808,7 +1810,7 @@ class ServersControllerCreateTest(test.TestCase): def test_create_instance_with_bad_config_drive(self): self.config_drive = "asdf" - image_href = 'http://localhost/v1.1/123/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { @@ -1833,7 +1835,7 @@ class ServersControllerCreateTest(test.TestCase): self.controller.create, req, body) def test_create_instance_without_config_drive(self): - image_href = 'http://localhost/v1.1/123/images/2' + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { @@ -1860,7 +1862,7 @@ class ServersControllerCreateTest(test.TestCase): self.assertFalse(server['config_drive']) def test_create_instance_bad_href(self): - image_href = 'http://localhost/v1.1/images/asdf' + image_href = 'asdf' flavor_ref = 'http://localhost/v1.1/flavors/3' body = dict(server=dict( name='server_test', imageRef=image_href, flavorRef=flavor_ref, @@ -1875,7 +1877,7 @@ class ServersControllerCreateTest(test.TestCase): self.controller.create, req, body) def test_create_instance_local_href(self): - image_id = "2" + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' flavor_ref = 'http://localhost/v1.1/flavors/3' expected_flavor = { "id": "3", @@ -1887,18 +1889,18 @@ class ServersControllerCreateTest(test.TestCase): ], } expected_image = { - "id": "2", + "id": image_uuid, "links": [ { "rel": "bookmark", - "href": 'http://localhost/fake/images/2', + "href": 'http://localhost/fake/images/%s' % image_uuid, }, ], } body = { 'server': { 'name': 'server_test', - 'imageRef': image_id, + 'imageRef': image_uuid, 'flavorRef': flavor_ref, }, } @@ -1914,12 +1916,11 @@ class ServersControllerCreateTest(test.TestCase): self.assertEqual(expected_image, server['image']) def test_create_instance_admin_pass(self): - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' body = { 'server': { 'name': 'server_test', - 'imageRef': 3, + 'imageRef': image_uuid, 'flavorRef': 3, 'adminPass': 'testpass', }, diff --git a/nova/tests/api/openstack/test_urlmap.py b/nova/tests/api/openstack/test_urlmap.py index 49b03283d..3995765e5 100644 --- a/nova/tests/api/openstack/test_urlmap.py +++ b/nova/tests/api/openstack/test_urlmap.py @@ -61,20 +61,24 @@ class UrlmapTest(test.TestCase): def test_path_content_type(self): """Test URL path specifying JSON returns JSON content.""" - req = webob.Request.blank('/v1.1/foobar/images/1.json') + url = '/v1.1/foobar/images/cedef40a-ed67-4d10-800e-17455edce175.json' + req = webob.Request.blank(url) req.accept = "application/xml" res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) self.assertEqual(res.content_type, "application/json") body = json.loads(res.body) - self.assertEqual(body['image']['id'], '1') + self.assertEqual(body['image']['id'], + 'cedef40a-ed67-4d10-800e-17455edce175') def test_accept_content_type(self): """Test Accept header specifying JSON returns JSON content.""" - req = webob.Request.blank('/v1.1/foobar/images/1') + url = '/v1.1/foobar/images/cedef40a-ed67-4d10-800e-17455edce175' + req = webob.Request.blank(url) req.accept = "application/xml;q=0.8, application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) self.assertEqual(res.content_type, "application/json") body = json.loads(res.body) - self.assertEqual(body['image']['id'], '1') + self.assertEqual(body['image']['id'], + 'cedef40a-ed67-4d10-800e-17455edce175') diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 6c4de8481..13fb6c6ca 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -39,3 +39,4 @@ FLAGS['verbose'].SetDefault(True) FLAGS['sqlite_db'].SetDefault("tests.sqlite") FLAGS['use_ipv6'].SetDefault(True) FLAGS['flat_network_bridge'].SetDefault('br100') +FLAGS['sqlite_synchronous'].SetDefault(False) diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index 1ab40f5f6..2ab1897f9 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -541,18 +541,11 @@ class TestGlanceImageService(test.TestCase): fixture = self._make_fixture(name='test image') image_id = self.service.create(self.context, fixture)['id'] client, same_id = glance.get_glance_client(self.context, image_id) - self.assertEquals(same_id, int(image_id)) + self.assertEquals(same_id, image_id) def test_glance_client_image_ref(self): fixture = self._make_fixture(name='test image') image_id = self.service.create(self.context, fixture)['id'] image_url = 'http://foo/%s' % image_id client, same_id = glance.get_glance_client(self.context, image_url) - self.assertEquals(same_id, int(image_id)) - - def test_glance_client_invalid_image_ref(self): - fixture = self._make_fixture(name='test image') - image_id = self.service.create(self.context, fixture)['id'] - image_url = 'khan' - self.assertRaises(exception.InvalidImageRef, glance.get_glance_client, - self.context, 'khan') + self.assertEquals(same_id, image_id) diff --git a/nova/tests/image/test_s3.py b/nova/tests/image/test_s3.py index f1ceeb7fe..fbd2738f8 100644 --- a/nova/tests/image/test_s3.py +++ b/nova/tests/image/test_s3.py @@ -16,6 +16,8 @@ # under the License. from nova import context +import nova.db.api +from nova import exception from nova import test from nova.image import s3 @@ -60,6 +62,10 @@ class TestS3ImageService(test.TestCase): self.image_service = s3.S3ImageService() self.context = context.RequestContext(None, None) + # set up one fixture to test shows, should have id '1' + nova.db.api.s3_image_create(self.context, + '155d900f-4e14-4e4c-a73d-069cbf4541e6') + def _assertEqualList(self, list0, list1, keys): self.assertEqual(len(list0), len(list1)) key = keys[0] @@ -72,6 +78,14 @@ class TestS3ImageService(test.TestCase): for k in keys: self.assertEqual(x[k], y[k]) + def test_show_cannot_use_uuid(self): + self.assertRaises(exception.ImageNotFound, + self.image_service.show, self.context, + '155d900f-4e14-4e4c-a73d-069cbf4541e6') + + def test_show_translates_correctly(self): + image = self.image_service.show(self.context, '1') + def test_s3_create(self): metadata = {'properties': { 'root_device_name': '/dev/sda1', @@ -83,11 +97,10 @@ class TestS3ImageService(test.TestCase): 'virutal_name': 'ephemeral0'}, {'device_name': '/dev/sdb0', 'no_device': True}]}} - _manifest, image = self.image_service._s3_parse_manifest( + _manifest, image, image_uuid = self.image_service._s3_parse_manifest( self.context, metadata, ami_manifest_xml) - image_id = image['id'] - ret_image = self.image_service.show(self.context, image_id) + ret_image = self.image_service.show(self.context, image['id']) self.assertTrue('properties' in ret_image) properties = ret_image['properties'] diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py index b53e4cec6..5351c53f6 100644 --- a/nova/tests/integrated/integrated_helpers.py +++ b/nova/tests/integrated/integrated_helpers.py @@ -22,11 +22,12 @@ Provides common functionality for integrated unit tests import random import string -from nova import service -from nova import test # For the flags import nova.image.glance from nova.log import logging +from nova import service +from nova import test # For the flags from nova.tests.integrated.api import client +from nova import utils LOG = logging.getLogger('nova.tests.integrated') @@ -65,7 +66,7 @@ class _IntegratedTestBase(test.TestCase): self.flags(verbose=True) def fake_get_image_service(context, image_href): - image_id = int(str(image_href).split('/')[-1]) + image_id = str(image_href).split('/')[-1] return (nova.image.fake.FakeImageService(), image_id) self.stubs.Set(nova.image, 'get_image_service', fake_get_image_service) @@ -103,9 +104,7 @@ class _IntegratedTestBase(test.TestCase): return generate_new_element(server_names, 'server') def get_invalid_image(self): - images = self.api.get_images() - image_ids = [image['id'] for image in images] - return generate_new_element(image_ids, '', numeric=True) + return str(utils.gen_uuid()) def _build_minimal_create_server_request(self): server = {} diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index 8eb45d5f3..36f62ac01 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -336,7 +336,7 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # rebuild the server with metadata post = {} post['rebuild'] = { - "imageRef": "https://localhost/v1.1/32278/images/3", + "imageRef": "c905cedb-7281-47e4-8a62-f26bc5fc4c77", "name": "blah", } @@ -348,7 +348,8 @@ class ServersTest(integrated_helpers._IntegratedTestBase): self.assertEqual(created_server_id, found_server['id']) self.assertEqual({}, found_server.get('metadata')) self.assertEqual('blah', found_server.get('name')) - self.assertEqual('3', found_server.get('image')['id']) + self.assertEqual(post['rebuild']['imageRef'], + found_server.get('image')['id']) # Cleanup self._delete_server(created_server_id) @@ -370,7 +371,7 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # rebuild the server with metadata post = {} post['rebuild'] = { - "imageRef": "https://localhost/v1.1/32278/images/2", + "imageRef": "76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", "name": "blah", } @@ -416,7 +417,7 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # rebuild the server with metadata post = {} post['rebuild'] = { - "imageRef": "https://localhost/v1.1/32278/images/2", + "imageRef": "76fa36fc-c930-4bf3-8c8a-ea2a2420deb6", "name": "blah", } diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py index 6f708691b..df1ccce61 100644 --- a/nova/tests/scheduler/test_scheduler.py +++ b/nova/tests/scheduler/test_scheduler.py @@ -61,7 +61,7 @@ def _create_instance_dict(**kwargs): inst = {} # NOTE(jk0): If an integer is passed as the image_ref, the image # service will use the default image service (in this case, the fake). - inst['image_ref'] = '1' + inst['image_ref'] = 'cedef40a-ed67-4d10-800e-17455edce175' inst['reservation_id'] = 'r-fakeres' inst['user_id'] = kwargs.get('user_id', 'admin') inst['project_id'] = kwargs.get('project_id', 'fake') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 7faf81602..addb6084d 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -496,13 +496,14 @@ class ComputeTestCase(test.TestCase): instance_id = self._create_instance() self.compute.run_instance(self.context, instance_id) self.assertEquals(len(test_notifier.NOTIFICATIONS), 1) + inst_ref = db.instance_get(self.context, instance_id) msg = test_notifier.NOTIFICATIONS[0] 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['instance_id'], instance_id) + self.assertEquals(payload['instance_id'], inst_ref.uuid) self.assertEquals(payload['instance_type'], 'm1.tiny') type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] self.assertEquals(str(payload['instance_type_id']), str(type_id)) @@ -515,6 +516,7 @@ class ComputeTestCase(test.TestCase): def test_terminate_usage_notification(self): """Ensure terminate_instance generates apropriate usage notification""" instance_id = self._create_instance() + inst_ref = db.instance_get(self.context, instance_id) self.compute.run_instance(self.context, instance_id) test_notifier.NOTIFICATIONS = [] self.compute.terminate_instance(self.context, instance_id) @@ -530,7 +532,7 @@ class ComputeTestCase(test.TestCase): payload = msg['payload'] 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_id'], inst_ref.uuid) self.assertEquals(payload['instance_type'], 'm1.tiny') type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] self.assertEquals(str(payload['instance_type_id']), str(type_id)) @@ -613,7 +615,7 @@ class ComputeTestCase(test.TestCase): payload = msg['payload'] 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_id'], inst_ref.uuid) self.assertEquals(payload['instance_type'], 'm1.tiny') type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] self.assertEquals(str(payload['instance_type_id']), str(type_id)) diff --git a/nova/tests/test_compute_utils.py b/nova/tests/test_compute_utils.py index 61d5cac06..5efb10166 100644 --- a/nova/tests/test_compute_utils.py +++ b/nova/tests/test_compute_utils.py @@ -85,7 +85,7 @@ class UsageInfoTestCase(test.TestCase): payload = msg['payload'] 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_id'], instance.uuid) self.assertEquals(payload['instance_type'], 'm1.tiny') type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] self.assertEquals(str(payload['instance_type_id']), str(type_id)) diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py index 6f6269e52..590505a23 100644 --- a/nova/tests/test_db_api.py +++ b/nova/tests/test_db_api.py @@ -147,3 +147,12 @@ class DbApiTestCase(test.TestCase): results = db.instance_get_all_hung_in_rebooting(ctxt, 10) self.assertEqual(0, len(results)) db.instance_update(ctxt, instance.id, {"task_state": None}) + + def test_network_create_safe(self): + ctxt = context.get_admin_context() + values = {'host': 'localhost', 'project_id': 'project1'} + network = db.network_create_safe(ctxt, values) + self.assertNotEqual(None, network.uuid) + self.assertEqual(36, len(network.uuid)) + db_network = db.network_get(ctxt, network.id) + self.assertEqual(network.uuid, db_network.uuid) diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 29d604e06..1a199131d 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -61,6 +61,9 @@ class FakeQuantumClientConnection(object): except KeyError: return False + def get_network_name(self, tenant_id, net_id): + return self.nets[net_id]['net-name'] + def _confirm_not_attached(self, interface_id): for n in self.nets.values(): for p in n['ports'].values(): diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 35d48dd62..6b39adab2 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -72,7 +72,7 @@ class QuotaTestCase(test.TestCase): def _create_instance(self, cores=2): """Create a test instance""" inst = {} - inst['image_id'] = 1 + inst['image_id'] = 'cedef40a-ed67-4d10-800e-17455edce175' inst['reservation_id'] = 'r-fakeres' inst['user_id'] = self.user_id inst['project_id'] = self.project_id @@ -218,12 +218,13 @@ class QuotaTestCase(test.TestCase): instance_id = self._create_instance() instance_ids.append(instance_id) inst_type = instance_types.get_instance_type_by_name('m1.small') + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, instance_type=inst_type, - image_href=1) + image_href=image_uuid) for instance_id in instance_ids: db.instance_destroy(self.context, instance_id) @@ -232,12 +233,13 @@ class QuotaTestCase(test.TestCase): instance_id = self._create_instance(cores=4) instance_ids.append(instance_id) inst_type = instance_types.get_instance_type_by_name('m1.small') + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, instance_type=inst_type, - image_href=1) + image_href=image_uuid) for instance_id in instance_ids: db.instance_destroy(self.context, instance_id) @@ -286,12 +288,13 @@ class QuotaTestCase(test.TestCase): for i in range(FLAGS.quota_metadata_items + 1): metadata['key%s' % i] = 'value%s' % i inst_type = instance_types.get_instance_type_by_name('m1.small') + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, instance_type=inst_type, - image_href='fake', + image_href=image_uuid, metadata=metadata) def test_default_allowed_injected_files(self): @@ -340,15 +343,19 @@ class QuotaTestCase(test.TestCase): self.flags(image_service='nova.image.fake.FakeImageService') api = compute.API(image_service=self.StubImageService()) inst_type = instance_types.get_instance_type_by_name('m1.small') + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' api.create(self.context, min_count=1, max_count=1, - instance_type=inst_type, image_href='3', + instance_type=inst_type, image_href=image_uuid, injected_files=files) def test_no_injected_files(self): self.flags(image_service='nova.image.fake.FakeImageService') api = compute.API(image_service=self.StubImageService()) inst_type = instance_types.get_instance_type_by_name('m1.small') - api.create(self.context, instance_type=inst_type, image_href='3') + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' + api.create(self.context, + instance_type=inst_type, + image_href=image_uuid) def test_max_injected_files(self): files = [] diff --git a/nova/utils.py b/nova/utils.py index ad585ba1c..9a01a6fb8 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -339,7 +339,7 @@ def usage_from_instance(instance_ref, **kw): usage_info = dict( tenant_id=instance_ref['project_id'], user_id=instance_ref['user_id'], - instance_id=instance_ref['id'], + instance_id=instance_ref['uuid'], instance_type=instance_ref['instance_type']['name'], instance_type_id=instance_ref['instance_type_id'], display_name=instance_ref['display_name'], diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 3e39482d8..f648af733 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -872,7 +872,7 @@ class LibvirtConnection(driver.ComputeDriver): 'ramdisk_id': inst['ramdisk_id']} if disk_images['kernel_id']: - fname = '%08x' % int(disk_images['kernel_id']) + fname = disk_images['kernel_id'] self._cache_image(fn=self._fetch_image, context=context, target=basepath('kernel'), @@ -881,7 +881,7 @@ class LibvirtConnection(driver.ComputeDriver): user_id=inst['user_id'], project_id=inst['project_id']) if disk_images['ramdisk_id']: - fname = '%08x' % int(disk_images['ramdisk_id']) + fname = disk_images['ramdisk_id'] self._cache_image(fn=self._fetch_image, context=context, target=basepath('ramdisk'), @@ -966,7 +966,7 @@ class LibvirtConnection(driver.ComputeDriver): target_partition = None if config_drive_id: - fname = '%08x' % int(config_drive_id) + fname = config_drive_id self._cache_image(fn=self._fetch_image, target=basepath('disk.config'), fname=fname, |