def test_create_image_with_metadata(self): body = {"createImage": {"name": "Snapshot 1", "metadata": {"key": "asdf"}}} response = self.controller._action_create_image(self.req, FAKE_UUID, body=body) location = response.headers["Location"] self.assertEqual(self.image_url + "123" if self.image_url else glance.generate_image_url("123"), location)
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ["nova.context"] authorize(context, action="create_image") entity = body["createImage"] image_name = entity["name"] metadata = entity.get("metadata", {}) common.check_img_metadata_properties_quota(context, metadata) instance = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(context, instance.uuid) try: if self.compute_api.is_volume_backed_instance(context, instance, bdms): authorize(context, action="create_image:allow_volume_backed") image = self.compute_api.snapshot_volume_backed( context, instance, image_name, extra_properties=metadata ) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=metadata) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "createImage", id) except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image["id"]) image_ref = glance.generate_image_url(image_id) resp = webob.Response(status_int=202) resp.headers["Location"] = image_ref return resp
def _get_bookmark_link(self, request, identifier, collection_name): """Create a URL that refers to a specific resource.""" if collection_name == "images": glance_url = glance.generate_image_url(identifier) return self._update_glance_link_prefix(glance_url) else: raise NotImplementedError
def snapshot(self, context, instance, image_id, update_task_state): """Snapshots the specified instance. :param context: security context :param instance: nova.objects.instance.Instance :param image_id: Reference to a pre-created image that will hold the snapshot. """ return config = self.load_config() azure_sms = self.get_management_service(ServiceManagementService, config=config) # Power off vm result = self.power_off(instance=instance) result.wait() hosted_service_name = 'compunovacloud' deployment_name = 'dep1' vm_name = 'vm1' image_name = instance.uuid + 'image' image = CaptureRoleAsVMImage('Specialized', image_name, image_name + 'label', image_name + 'description', 'english', 'openstack-virtual-machines') result = azure_sms.capture_vm_image(hosted_service_name, deployment_name, vm_name, image) image_service = glance.get_default_image_service() snapshot = image_service.show(context, image_id) LOG.debug("**** Snapshot info--> %s" % snapshot) snapshot_name = haikunator.haikunate() image_url = glance.generate_image_url(image_id) LOG.debug("**** image url--> '%s' ****" % image_url) image_metadata = { 'is_public': False, 'status': 'active', 'name': '-'.join(('azure', snapshot_name)), 'properties': { 'kernel_id': instance['kernel_id'], 'image_location': 'snapshot', 'image_state': 'available', 'ramdisk_id': instance['ramdisk_id'], 'owner_id': instance['project_id'] } } if instance['os_type']: image_metadata['properties']['os_type'] = instance['os_type'] update_task_state(task_state=task_states.IMAGE_UPLOADING, expected_state=task_states.IMAGE_SNAPSHOT) azure_sms.snapshot(service_name, vm_name, image_id, snapshot_name) image_service.update(context, image_id, image_metadata, "fake image data")
def test_create_image_with_metadata(self): body = {"create_image": {"name": "Snapshot 1", "metadata": {"key": "asdf"}}} req = fakes.HTTPRequestV3.blank(self.url) response = self.controller._action_create_image(req, FAKE_UUID, body) location = response.headers["Location"] self.assertEqual(glance.generate_image_url("123"), location)
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ['nova.context'] authorize(context, action='create_image') entity = body["createImage"] image_name = entity["name"] metadata = entity.get('metadata', {}) common.check_img_metadata_properties_quota(context, metadata) instance = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( context, instance.uuid) try: if self.compute_api.is_volume_backed_instance(context, instance, bdms): authorize(context, action="create_image:allow_volume_backed") img = instance.image_ref if not img: properties = bdms.root_metadata( context, self.compute_api.image_api, self.compute_api.volume_api) image_meta = {'properties': properties} else: image_meta = self.compute_api.image_api.get(context, img) image = self.compute_api.snapshot_volume_backed( context, instance, image_meta, image_name, extra_properties= metadata) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=metadata) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'createImage', id) except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image['id']) image_ref = glance.generate_image_url(image_id) resp = webob.Response(status_int=202) resp.headers['Location'] = image_ref return resp
def test_create_image(self): body = { 'create_image': { 'name': 'Snapshot 1', }, } req = fakes.HTTPRequestV3.blank(self.url) response = self.controller._action_create_image(req, FAKE_UUID, body) location = response.headers['Location'] self.assertEqual(glance.generate_image_url('123'), location)
def test_create_image(self): body = { 'createImage': { 'name': 'Snapshot 1', }, } req = fakes.HTTPRequestV3.blank(self.url) response = self.controller._action_create_image(req, FAKE_UUID, body) location = response.headers['Location'] self.assertEqual(glance.generate_image_url('123'), location)
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ['nova.context'] entity = body["createImage"] image_name = entity["name"] metadata = entity.get('metadata', {}) common.check_img_metadata_properties_quota(context, metadata) instance = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( context, instance.uuid) try: if self.compute_api.is_volume_backed_instance(context, instance, bdms): img = instance['image_ref'] if not img: properties = bdms.root_metadata( context, self.compute_api.image_api, self.compute_api.volume_api) image_meta = {'properties': properties} else: image_meta = self.compute_api.image_api.get(context, img) image = self.compute_api.snapshot_volume_backed( context, instance, image_meta, image_name, extra_properties= metadata) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=metadata) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'createImage', id) except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image['id']) image_ref = glance.generate_image_url(image_id) resp = webob.Response(status_int=202) resp.headers['Location'] = image_ref return resp
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ["nova.context"] entity = body.get("create_image", {}) image_name = entity.get("name") if not image_name: msg = _("create_image entity requires name attribute") raise exc.HTTPBadRequest(explanation=msg) props = {} metadata = entity.get("metadata", {}) common.check_img_metadata_properties_quota(context, metadata) try: props.update(metadata) except ValueError: msg = _("Invalid metadata") raise exc.HTTPBadRequest(explanation=msg) instance = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(context, instance.uuid) try: if self.compute_api.is_volume_backed_instance(context, instance, bdms): img = instance["image_ref"] if not img: properties = bdms.root_metadata(context, self.compute_api.image_api, self.compute_api.volume_api) image_meta = {"properties": properties} else: image_meta = self.compute_api.image_api.get(context, img) image = self.compute_api.snapshot_volume_backed( context, instance, image_meta, image_name, extra_properties=props ) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=props) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, "create_image") except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image["id"]) image_ref = glance.generate_image_url(image_id) resp = webob.Response(status_int=202) resp.headers["Location"] = image_ref return resp
def snapshot(self, context, instance, image_id, update_task_state): """Snapshot an image of the specified instance on EC2 and create an Image which gets stored in AMI (internally in EBS Snapshot) :param context: security context :param instance: nova.objects.instance.Instance :param image_id: Reference to a pre-created image that will hold the snapshot. """ if instance.metadata.get('ec2_id', None) is None: raise exception.InstanceNotFound(instance_id=instance['uuid']) # Adding the below line only alters the state of the instance and not # its image in OpenStack. update_task_state(task_state=task_states.IMAGE_UPLOADING, expected_state=task_states.IMAGE_SNAPSHOT) ec2_id = self._get_ec2_id_from_instance(instance) ec_instance_info = self.ec2_conn.get_only_instances( instance_ids=[ec2_id], filters=None, dry_run=False, max_results=None) ec2_instance = ec_instance_info[0] if ec2_instance.state == 'running': ec2_image_id = ec2_instance.create_image( name=str(image_id), description="Image created by OpenStack", no_reboot=False, dry_run=False) LOG.info("Image created: %s." % ec2_image_id) # The instance will be in pending state when it comes up, waiting # for it to be in available self._wait_for_image_state(ec2_image_id, "available") image_api = glance.get_default_image_service() image_ref = glance.generate_image_url(image_id) metadata = { 'is_public': False, 'location': image_ref, 'properties': { 'kernel_id': instance['kernel_id'], 'image_state': 'available', 'owner_id': instance['project_id'], 'ramdisk_id': instance['ramdisk_id'], 'ec2_image_id': ec2_image_id } } # TODO(jhurt): This currently fails, leaving the status of an instance # as 'snapshotting' image_api.update(context, image_id, metadata)
def test_create_image(self): body = { 'createImage': { 'name': 'Snapshot 1', }, } response = self.controller._action_create_image(self.req, FAKE_UUID, body=body) location = response.headers['Location'] self.assertEqual(self.image_url + '123' if self.image_url else glance.generate_image_url('123'), location)
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ['nova.context'] context.can(server_policies.SERVERS % 'create_image') entity = body["createImage"] image_name = common.normalize_name(entity["name"]) metadata = entity.get('metadata', {}) common.check_img_metadata_properties_quota(context, metadata) instance = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( context, instance.uuid) try: if compute_utils.is_volume_backed_instance(context, instance, bdms): context.can(server_policies.SERVERS % 'create_image:allow_volume_backed') image = self.compute_api.snapshot_volume_backed( context, instance, image_name, extra_properties= metadata) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=metadata) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'createImage', id) except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image['id']) image_ref = glance.generate_image_url(image_id) resp = webob.Response(status_int=202) resp.headers['Location'] = image_ref return resp
def snapshot(self, context, instance, image_id, update_task_state): """Snapshot an image of the specified instance on EC2 and create an Image which gets stored in AMI (internally in EBS Snapshot) :param context: security context :param instance: nova.objects.instance.Instance :param image_id: Reference to a pre-created image that will hold the snapshot. """ LOG.info("***** Calling SNAPSHOT *******************") if instance['metadata']['ec2_id'] is None: raise exception.InstanceNotRunning(instance_id=instance['uuid']) # Adding the below line only alters the state of the instance and not # its image in OpenStack. update_task_state( task_state=task_states.IMAGE_UPLOADING, expected_state=task_states.IMAGE_SNAPSHOT) ec2_id = instance['metadata']['ec2_id'] ec_instance_info = self.ec2_conn.get_only_instances( instance_ids=[ec2_id], filters=None, dry_run=False, max_results=None) ec2_instance = ec_instance_info[0] if ec2_instance.state == 'running': ec2_image_id = ec2_instance.create_image(name=str( image_id), description="Image from OpenStack", no_reboot=False, dry_run=False) LOG.info("Image has been created state to %s." % ec2_image_id) # The instance will be in pending state when it comes up, waiting forit to be in available self._wait_for_image_state(ec2_image_id, "available") image_api = glance.get_default_image_service() image_ref = glance.generate_image_url(image_id) metadata = {'is_public': False, 'location': image_ref, 'properties': { 'kernel_id': instance['kernel_id'], 'image_state': 'available', 'owner_id': instance['project_id'], 'ramdisk_id': instance['ramdisk_id'], 'ec2_image_id': ec2_image_id } } image_api.update(context, image_id, metadata)
def snapshot(self, context, instance, image_id, update_task_state): aws_node = self._get_node_by_uuid(instance.uuid) update_task_state(task_state=task_states.IMAGE_UPLOADING, expected_state=task_states.IMAGE_SNAPSHOT) if aws_node.state['Name'] == 'running': ec2_image = aws_node.create_image( Name=str(image_id), Description="Image from OpenStack", NoReboot=False, DryRun=False) else: # TODO: else case LOG.error( 'Node state: "{}". Must be "running" for snapshot.'.format( aws_node.state['Name'])) return # The instance will be in pending state when it comes up, waiting for it to be in available self._wait_for_image_state(ec2_image, "available") image_api = glance.get_default_image_service() image_ref = glance.generate_image_url(image_id) metadata = { 'is_public': False, 'location': image_ref, 'properties': { 'kernel_id': instance['kernel_id'], 'image_state': 'available', 'owner_id': instance['project_id'], 'ramdisk_id': instance['ramdisk_id'], 'ec2_image_id': ec2_image.id } } # TODO: HTTPInternalServerError: 500 Internal Server Error: # TODO: The server has either erred or is incapable of performing the requested operation. image_api.update(context, image_id, metadata)
def info_from_instance(context, instance, network_info, system_metadata, **kw): """Get detailed instance information for an instance which is common to all notifications. :param:instance: nova.objects.Instance :param:network_info: network_info provided if not None :param:system_metadata: system_metadata DB entries for the instance, if not None .. note:: Currently unused here in trunk, but needed for potential custom modifications. """ def null_safe_str(s): return str(s) if s else '' def null_safe_int(s): return int(s) if s else '' def null_safe_isotime(s): if isinstance(s, datetime.datetime): return timeutils.strtime(s) else: return str(s) if s else '' image_ref_url = glance.generate_image_url(instance.image_ref) instance_type = instance.get_flavor() instance_type_name = instance_type.get('name', '') instance_flavorid = instance_type.get('flavorid', '') instance_info = dict( # Owner properties tenant_id=instance.project_id, user_id=instance.user_id, # Identity properties instance_id=instance.uuid, display_name=instance.display_name, reservation_id=instance.reservation_id, hostname=instance.hostname, # Type properties instance_type=instance_type_name, instance_type_id=instance.instance_type_id, instance_flavor_id=instance_flavorid, architecture=instance.architecture, # Capacity properties memory_mb=instance.memory_mb, disk_gb=instance.root_gb + instance.ephemeral_gb, vcpus=instance.vcpus, # Note(dhellmann): This makes the disk_gb value redundant, but # we are keeping it for backwards-compatibility with existing # users of notifications. root_gb=instance.root_gb, ephemeral_gb=instance.ephemeral_gb, # Location properties host=instance.host, node=instance.node, availability_zone=instance.availability_zone, cell_name=null_safe_str(instance.cell_name), # Date properties created_at=str(instance.created_at), # Terminated and Deleted are slightly different (although being # terminated and not deleted is a transient state), so include # both and let the recipient decide which they want to use. terminated_at=null_safe_isotime(instance.get('terminated_at', None)), deleted_at=null_safe_isotime(instance.get('deleted_at', None)), launched_at=null_safe_isotime(instance.get('launched_at', None)), # Image properties image_ref_url=image_ref_url, os_type=instance.os_type, kernel_id=instance.kernel_id, ramdisk_id=instance.ramdisk_id, # Status properties state=instance.vm_state, state_description=null_safe_str(instance.task_state), progress=null_safe_int(instance.progress), # accessIPs access_ip_v4=instance.access_ip_v4, access_ip_v6=instance.access_ip_v6, ) if network_info is not None: fixed_ips = [] for vif in network_info: for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] ip["vif_mac"] = vif["address"] fixed_ips.append(ip) instance_info['fixed_ips'] = fixed_ips # add image metadata image_meta_props = image_meta(instance.system_metadata) instance_info["image_meta"] = image_meta_props # add instance metadata instance_info['metadata'] = instance.metadata instance_info.update(kw) return instance_info
def _do_test_create_volume_backed_image(self, extra_properties): def _fake_id(x): return '%s-%s-%s-%s' % (x * 8, x * 4, x * 4, x * 12) body = dict(createImage=dict(name='snapshot_of_volume_backed')) if extra_properties: body['createImage']['metadata'] = extra_properties image_service = glance.get_default_image_service() bdm = [dict(volume_id=_fake_id('a'), volume_size=1, device_name='vda', delete_on_termination=False)] props = dict(kernel_id=_fake_id('b'), ramdisk_id=_fake_id('c'), root_device_name='/dev/vda', block_device_mapping=bdm) original_image = dict(properties=props, container_format='ami', status='active', is_public=True) image_service.create(None, original_image) def fake_block_device_mapping_get_all_by_instance(context, inst_id, use_slave=False): return [fake_block_device.FakeDbBlockDeviceDict( {'volume_id': _fake_id('a'), 'source_type': 'snapshot', 'destination_type': 'volume', 'volume_size': 1, 'device_name': 'vda', 'snapshot_id': 1, 'boot_index': 0, 'delete_on_termination': False, 'no_device': None})] self.stubs.Set(db, 'block_device_mapping_get_all_by_instance', fake_block_device_mapping_get_all_by_instance) instance = fakes.fake_instance_get(image_ref=original_image['id'], vm_state=vm_states.ACTIVE, root_device_name='/dev/vda') self.stubs.Set(db, 'instance_get_by_uuid', instance) self.mox.StubOutWithMock(self.controller.compute_api.compute_rpcapi, 'quiesce_instance') self.controller.compute_api.compute_rpcapi.quiesce_instance( mox.IgnoreArg(), mox.IgnoreArg()).AndRaise( exception.InstanceQuiesceNotSupported(instance_id='fake', reason='test')) volume = dict(id=_fake_id('a'), size=1, host='fake', display_description='fake') snapshot = dict(id=_fake_id('d')) self.mox.StubOutWithMock(self.controller.compute_api, 'volume_api') volume_api = self.controller.compute_api.volume_api volume_api.get(mox.IgnoreArg(), volume['id']).AndReturn(volume) volume_api.create_snapshot_force(mox.IgnoreArg(), volume['id'], mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(snapshot) self.mox.ReplayAll() response = self.controller._action_create_image(self.req, FAKE_UUID, body=body) location = response.headers['Location'] image_id = location.replace(self.image_url or glance.generate_image_url(''), '') image = image_service.show(None, image_id) self.assertEqual(image['name'], 'snapshot_of_volume_backed') properties = image['properties'] self.assertEqual(properties['kernel_id'], _fake_id('b')) self.assertEqual(properties['ramdisk_id'], _fake_id('c')) self.assertEqual(properties['root_device_name'], '/dev/vda') self.assertEqual(properties['bdm_v2'], True) bdms = properties['block_device_mapping'] self.assertEqual(len(bdms), 1) self.assertEqual(bdms[0]['boot_index'], 0) self.assertEqual(bdms[0]['source_type'], 'snapshot') self.assertEqual(bdms[0]['destination_type'], 'volume') self.assertEqual(bdms[0]['snapshot_id'], snapshot['id']) for fld in ('connection_info', 'id', 'instance_uuid', 'device_name'): self.assertNotIn(fld, bdms[0]) for k in extra_properties.keys(): self.assertEqual(properties[k], extra_properties[k])
def info_from_instance(context, instance_ref, network_info, system_metadata, **kw): """Get detailed instance information for an instance which is common to all notifications. :param network_info: network_info provided if not None :param system_metadata: system_metadata DB entries for the instance, if not None. *NOTE*: Currently unused here in trunk, but needed for potential custom modifications. """ def null_safe_str(s): return str(s) if s else "" image_ref_url = glance.generate_image_url(instance_ref["image_ref"]) instance_type_name = instance_ref.get("instance_type", {}).get("name", "") if system_metadata is None: system_metadata = utils.metadata_to_dict(instance_ref["system_metadata"]) instance_info = dict( # Owner properties tenant_id=instance_ref["project_id"], user_id=instance_ref["user_id"], # Identity properties instance_id=instance_ref["uuid"], display_name=instance_ref["display_name"], reservation_id=instance_ref["reservation_id"], hostname=instance_ref["hostname"], # Type properties instance_type=instance_type_name, instance_type_id=instance_ref["instance_type_id"], architecture=instance_ref["architecture"], # Capacity properties memory_mb=instance_ref["memory_mb"], disk_gb=instance_ref["root_gb"] + instance_ref["ephemeral_gb"], vcpus=instance_ref["vcpus"], # Note(dhellmann): This makes the disk_gb value redundant, but # we are keeping it for backwards-compatibility with existing # users of notifications. root_gb=instance_ref["root_gb"], ephemeral_gb=instance_ref["ephemeral_gb"], # Location properties host=instance_ref["host"], availability_zone=instance_ref["availability_zone"], # Date properties created_at=str(instance_ref["created_at"]), # Nova's deleted vs terminated instance terminology is confusing, # this should be when the instance was deleted (i.e. terminated_at), # not when the db record was deleted. (mdragon) deleted_at=null_safe_str(instance_ref.get("terminated_at")), launched_at=null_safe_str(instance_ref.get("launched_at")), # Image properties image_ref_url=image_ref_url, os_type=instance_ref["os_type"], kernel_id=instance_ref["kernel_id"], ramdisk_id=instance_ref["ramdisk_id"], # Status properties state=instance_ref["vm_state"], state_description=null_safe_str(instance_ref.get("task_state")), # accessIPs access_ip_v4=instance_ref["access_ip_v4"], access_ip_v6=instance_ref["access_ip_v6"], ) if network_info is not None: fixed_ips = [] for vif in network_info: for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] fixed_ips.append(ip) instance_info["fixed_ips"] = fixed_ips # add image metadata image_meta_props = image_meta(system_metadata) instance_info["image_meta"] = image_meta_props # add instance metadata instance_info["metadata"] = instance_ref["metadata"] instance_info.update(kw) return instance_info
def info_from_instance(context, instance, network_info, system_metadata, **kw): """Get detailed instance information for an instance which is common to all notifications. :param:instance: nova.objects.Instance :param:network_info: network_info provided if not None :param:system_metadata: system_metadata DB entries for the instance, if not None .. note:: Currently unused here in trunk, but needed for potential custom modifications. """ image_ref_url = glance.generate_image_url(instance.image_ref) instance_type = instance.get_flavor() instance_type_name = instance_type.get('name', '') instance_flavorid = instance_type.get('flavorid', '') instance_info = dict( # Owner properties tenant_id=instance.project_id, user_id=instance.user_id, # Identity properties instance_id=instance.uuid, display_name=instance.display_name, reservation_id=instance.reservation_id, hostname=instance.hostname, # Type properties instance_type=instance_type_name, instance_type_id=instance.instance_type_id, instance_flavor_id=instance_flavorid, architecture=instance.architecture, # Capacity properties memory_mb=instance.flavor.memory_mb, disk_gb=instance.flavor.root_gb + instance.flavor.ephemeral_gb, vcpus=instance.flavor.vcpus, # Note(dhellmann): This makes the disk_gb value redundant, but # we are keeping it for backwards-compatibility with existing # users of notifications. root_gb=instance.flavor.root_gb, ephemeral_gb=instance.flavor.ephemeral_gb, # Location properties host=instance.host, node=instance.node, availability_zone=instance.availability_zone, cell_name=null_safe_str(instance.cell_name), # Date properties created_at=str(instance.created_at), # Terminated and Deleted are slightly different (although being # terminated and not deleted is a transient state), so include # both and let the recipient decide which they want to use. terminated_at=null_safe_isotime(instance.get('terminated_at', None)), deleted_at=null_safe_isotime(instance.get('deleted_at', None)), launched_at=null_safe_isotime(instance.get('launched_at', None)), # Image properties image_ref_url=image_ref_url, os_type=instance.os_type, kernel_id=instance.kernel_id, ramdisk_id=instance.ramdisk_id, # Status properties state=instance.vm_state, state_description=null_safe_str(instance.task_state), # NOTE(gibi): It might seems wrong to default the progress to an empty # string but this is how legacy work and this code only used by the # legacy notification so try to keep the compatibility here but also # keep it contained. progress=int(instance.progress) if instance.progress else '', # accessIPs access_ip_v4=instance.access_ip_v4, access_ip_v6=instance.access_ip_v6, ) if network_info is not None: fixed_ips = [] for vif in network_info: for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] ip["vif_mac"] = vif["address"] fixed_ips.append(ip) instance_info['fixed_ips'] = fixed_ips # add image metadata image_meta_props = image_meta(instance.system_metadata) instance_info["image_meta"] = image_meta_props # add instance metadata instance_info['metadata'] = instance.metadata instance_info.update(kw) return instance_info
def _action_create_image(self, req, id, body): """Snapshot a server instance.""" context = req.environ['nova.context'] entity = body.get("create_image", {}) image_name = entity.get("name") if not image_name: msg = _("create_image entity requires name attribute") raise exc.HTTPBadRequest(explanation=msg) props = {} metadata = entity.get('metadata', {}) common.check_img_metadata_properties_quota(context, metadata) try: props.update(metadata) except ValueError: msg = _("Invalid metadata") raise exc.HTTPBadRequest(explanation=msg) instance = self._get_server(context, req, id) bdms = block_device_obj.BlockDeviceMappingList.get_by_instance_uuid( context, instance.uuid) try: if self.compute_api.is_volume_backed_instance( context, instance, bdms): img = instance['image_ref'] if not img: props = bdms.root_metadata(context, self.compute_api.image_service, self.compute_api.volume_api) image_meta = {'properties': props} else: src_image = self.compute_api.\ image_service.show(context, img) image_meta = dict(src_image) image = self.compute_api.snapshot_volume_backed( context, instance, image_meta, image_name, extra_properties=props) else: image = self.compute_api.snapshot(context, instance, image_name, extra_properties=props) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state( state_error, 'create_image') except exception.Invalid as err: raise exc.HTTPBadRequest(explanation=err.format_message()) # build location of newly-created image entity image_id = str(image['id']) image_ref = glance.generate_image_url(image_id) resp = webob.Response(status_int=202) resp.headers['Location'] = image_ref return resp
def info_from_instance(context, instance_ref, network_info, system_metadata, **kw): """Get detailed instance information for an instance which is common to all notifications. :param network_info: network_info provided if not None :param system_metadata: system_metadata DB entries for the instance, if not None. *NOTE*: Currently unused here in trunk, but needed for potential custom modifications. """ def null_safe_str(s): return str(s) if s else '' image_ref_url = glance.generate_image_url(instance_ref['image_ref']) instance_type = instance_types.extract_instance_type(instance_ref) instance_type_name = instance_type.get('name', '') if system_metadata is None: system_metadata = utils.metadata_to_dict( instance_ref['system_metadata']) instance_info = dict( # Owner properties tenant_id=instance_ref['project_id'], user_id=instance_ref['user_id'], # Identity properties instance_id=instance_ref['uuid'], display_name=instance_ref['display_name'], reservation_id=instance_ref['reservation_id'], hostname=instance_ref['hostname'], # Type properties instance_type=instance_type_name, instance_type_id=instance_ref['instance_type_id'], architecture=instance_ref['architecture'], # Capacity properties memory_mb=instance_ref['memory_mb'], disk_gb=instance_ref['root_gb'] + instance_ref['ephemeral_gb'], vcpus=instance_ref['vcpus'], # Note(dhellmann): This makes the disk_gb value redundant, but # we are keeping it for backwards-compatibility with existing # users of notifications. root_gb=instance_ref['root_gb'], ephemeral_gb=instance_ref['ephemeral_gb'], # Location properties host=instance_ref['host'], availability_zone=instance_ref['availability_zone'], # Date properties created_at=str(instance_ref['created_at']), # Nova's deleted vs terminated instance terminology is confusing, # this should be when the instance was deleted (i.e. terminated_at), # not when the db record was deleted. (mdragon) deleted_at=null_safe_str(instance_ref.get('terminated_at')), launched_at=null_safe_str(instance_ref.get('launched_at')), # Image properties image_ref_url=image_ref_url, os_type=instance_ref['os_type'], kernel_id=instance_ref['kernel_id'], ramdisk_id=instance_ref['ramdisk_id'], # Status properties state=instance_ref['vm_state'], state_description=null_safe_str(instance_ref.get('task_state')), # accessIPs access_ip_v4=instance_ref['access_ip_v4'], access_ip_v6=instance_ref['access_ip_v6'], ) if network_info is not None: fixed_ips = [] for vif in network_info: for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] fixed_ips.append(ip) instance_info['fixed_ips'] = fixed_ips # add image metadata image_meta_props = image_meta(system_metadata) instance_info["image_meta"] = image_meta_props # add instance metadata instance_info['metadata'] = instance_ref['metadata'] instance_info.update(kw) return instance_info
def _do_test_create_volume_backed_image( self, extra_properties, mock_vol_create_side_effect=None): def _fake_id(x): return '%s-%s-%s-%s' % (x * 8, x * 4, x * 4, x * 12) body = dict(createImage=dict(name='snapshot_of_volume_backed')) if extra_properties: body['createImage']['metadata'] = extra_properties image_service = glance.get_default_image_service() bdm = [dict(volume_id=_fake_id('a'), volume_size=1, device_name='vda', delete_on_termination=False)] def fake_block_device_mapping_get_all_by_instance(context, inst_id, use_slave=False): return [fake_block_device.FakeDbBlockDeviceDict( {'volume_id': _fake_id('a'), 'source_type': 'snapshot', 'destination_type': 'volume', 'volume_size': 1, 'device_name': 'vda', 'snapshot_id': 1, 'boot_index': 0, 'delete_on_termination': False, 'no_device': None})] self.stub_out('nova.db.block_device_mapping_get_all_by_instance', fake_block_device_mapping_get_all_by_instance) system_metadata = dict(image_kernel_id=_fake_id('b'), image_ramdisk_id=_fake_id('c'), image_root_device_name='/dev/vda', image_block_device_mapping=str(bdm), image_container_format='ami') instance = fakes.fake_instance_get(image_ref=uuids.fake, vm_state=vm_states.ACTIVE, root_device_name='/dev/vda', system_metadata=system_metadata) self.stub_out('nova.db.instance_get_by_uuid', instance) volume = dict(id=_fake_id('a'), size=1, host='fake', display_description='fake') snapshot = dict(id=_fake_id('d')) with test.nested( mock.patch.object(self.controller.compute_api.compute_rpcapi, 'quiesce_instance', side_effect=exception.InstanceQuiesceNotSupported( instance_id='fake', reason='test')), mock.patch.object(self.controller.compute_api.volume_api, 'get', return_value=volume), mock.patch.object(self.controller.compute_api.volume_api, 'create_snapshot_force', return_value=snapshot), ) as (mock_quiesce, mock_vol_get, mock_vol_create): if mock_vol_create_side_effect: mock_vol_create.side_effect = mock_vol_create_side_effect response = self.controller._action_create_image(self.req, FAKE_UUID, body=body) location = response.headers['Location'] image_id = location.replace(self.image_url or glance.generate_image_url(''), '') image = image_service.show(None, image_id) self.assertEqual(image['name'], 'snapshot_of_volume_backed') properties = image['properties'] self.assertEqual(properties['kernel_id'], _fake_id('b')) self.assertEqual(properties['ramdisk_id'], _fake_id('c')) self.assertEqual(properties['root_device_name'], '/dev/vda') self.assertTrue(properties['bdm_v2']) bdms = properties['block_device_mapping'] self.assertEqual(len(bdms), 1) self.assertEqual(bdms[0]['boot_index'], 0) self.assertEqual(bdms[0]['source_type'], 'snapshot') self.assertEqual(bdms[0]['destination_type'], 'volume') self.assertEqual(bdms[0]['snapshot_id'], snapshot['id']) self.assertEqual('/dev/vda', bdms[0]['device_name']) for fld in ('connection_info', 'id', 'instance_uuid'): self.assertNotIn(fld, bdms[0]) for k in extra_properties.keys(): self.assertEqual(properties[k], extra_properties[k]) mock_quiesce.assert_called_once_with(mock.ANY, mock.ANY) mock_vol_get.assert_called_once_with(mock.ANY, volume['id']) mock_vol_create.assert_called_once_with(mock.ANY, volume['id'], mock.ANY, mock.ANY)
def info_from_instance(context, instance_ref, network_info, system_metadata, **kw): """Get detailed instance information for an instance which is common to all notifications. :param network_info: network_info provided if not None :param system_metadata: system_metadata DB entries for the instance, if not None. *NOTE*: Currently unused here in trunk, but needed for potential custom modifications. """ def null_safe_str(s): return str(s) if s else "" def null_safe_isotime(s): if isinstance(s, datetime.datetime): return timeutils.strtime(s) else: return str(s) if s else "" image_ref_url = glance.generate_image_url(instance_ref["image_ref"]) instance_type = flavors.extract_flavor(instance_ref) instance_type_name = instance_type.get("name", "") instance_flavorid = instance_type.get("flavorid", "") if system_metadata is None: system_metadata = utils.instance_sys_meta(instance_ref) instance_info = dict( # Owner properties tenant_id=instance_ref["project_id"], user_id=instance_ref["user_id"], # Identity properties instance_id=instance_ref["uuid"], display_name=instance_ref["display_name"], reservation_id=instance_ref["reservation_id"], hostname=instance_ref["hostname"], # Type properties instance_type=instance_type_name, instance_type_id=instance_ref["instance_type_id"], instance_flavor_id=instance_flavorid, architecture=instance_ref["architecture"], # Capacity properties memory_mb=instance_ref["memory_mb"], disk_gb=instance_ref["root_gb"] + instance_ref["ephemeral_gb"], vcpus=instance_ref["vcpus"], # Note(dhellmann): This makes the disk_gb value redundant, but # we are keeping it for backwards-compatibility with existing # users of notifications. root_gb=instance_ref["root_gb"], ephemeral_gb=instance_ref["ephemeral_gb"], # Location properties host=instance_ref["host"], node=instance_ref["node"], availability_zone=instance_ref["availability_zone"], # Date properties created_at=str(instance_ref["created_at"]), # Terminated and Deleted are slightly different (although being # terminated and not deleted is a transient state), so include # both and let the recipient decide which they want to use. terminated_at=null_safe_isotime(instance_ref.get("terminated_at")), deleted_at=null_safe_isotime(instance_ref.get("deleted_at")), launched_at=null_safe_isotime(instance_ref.get("launched_at")), # Image properties image_ref_url=image_ref_url, os_type=instance_ref["os_type"], kernel_id=instance_ref["kernel_id"], ramdisk_id=instance_ref["ramdisk_id"], # Status properties state=instance_ref["vm_state"], state_description=null_safe_str(instance_ref.get("task_state")), # accessIPs access_ip_v4=instance_ref["access_ip_v4"], access_ip_v6=instance_ref["access_ip_v6"], ) if network_info is not None: fixed_ips = [] for vif in network_info: for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] fixed_ips.append(ip) instance_info["fixed_ips"] = fixed_ips # add image metadata image_meta_props = image_meta(system_metadata) instance_info["image_meta"] = image_meta_props # add instance metadata instance_info["metadata"] = utils.instance_meta(instance_ref) instance_info.update(kw) return instance_info
def info_from_instance(context, instance, network_info, system_metadata, **kw): """Get detailed instance information for an instance which is common to all notifications. :param:instance: nova.objects.Instance :param:network_info: network_info provided if not None :param:system_metadata: system_metadata DB entries for the instance, if not None .. note:: Currently unused here in trunk, but needed for potential custom modifications. """ def null_safe_str(s): return str(s) if s else '' def null_safe_int(s): return int(s) if s else '' def null_safe_isotime(s): if isinstance(s, datetime.datetime): return timeutils.strtime(s) else: return str(s) if s else '' image_ref_url = glance.generate_image_url(instance.image_ref) instance_type = instance.get_flavor() # # pf9: Get the instance type from sys metadata if it's stashed. If not, # # fall back to fetching it via the object API # try: # instance_type = flavors.extract_flavor(instance) # except KeyError: # instance_type_id = instance['instance_type_id'] # instance_type = objects.Flavor.get_by_id(context, instance_type_id) instance_type_name = instance_type.get('name', '') instance_flavorid = instance_type.get('flavorid', '') instance_info = dict( # Owner properties tenant_id=instance.project_id, user_id=instance.user_id, # Identity properties instance_id=instance.uuid, display_name=instance.display_name, reservation_id=instance.reservation_id, hostname=instance.hostname, # Type properties instance_type=instance_type_name, instance_type_id=instance.instance_type_id, instance_flavor_id=instance_flavorid, architecture=instance.architecture, # Capacity properties memory_mb=instance.memory_mb, disk_gb=instance.root_gb + instance.ephemeral_gb, vcpus=instance.vcpus, # Note(dhellmann): This makes the disk_gb value redundant, but # we are keeping it for backwards-compatibility with existing # users of notifications. root_gb=instance.root_gb, ephemeral_gb=instance.ephemeral_gb, # Location properties host=instance.host, node=instance.node, availability_zone=instance.availability_zone, cell_name=null_safe_str(instance.cell_name), # Date properties created_at=str(instance.created_at), # Terminated and Deleted are slightly different (although being # terminated and not deleted is a transient state), so include # both and let the recipient decide which they want to use. terminated_at=null_safe_isotime(instance.get('terminated_at', None)), deleted_at=null_safe_isotime(instance.get('deleted_at', None)), launched_at=null_safe_isotime(instance.get('launched_at', None)), # PF9 begin updated_at = null_safe_isotime(instance.get('updated_at', None)), # PF9 end # Image properties image_ref_url=image_ref_url, os_type=instance.os_type, kernel_id=instance.kernel_id, ramdisk_id=instance.ramdisk_id, # Status properties state=instance.vm_state, state_description=null_safe_str(instance.task_state), progress=null_safe_int(instance.progress), # accessIPs access_ip_v4=instance.access_ip_v4, access_ip_v6=instance.access_ip_v6, ) if network_info is not None: fixed_ips = [] for vif in network_info: for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] ip["vif_mac"] = vif["address"] fixed_ips.append(ip) instance_info['fixed_ips'] = fixed_ips # add image metadata image_meta_props = image_meta(instance.system_metadata) instance_info["image_meta"] = image_meta_props # add instance metadata instance_info['metadata'] = instance.metadata instance_info.update(kw) return instance_info
def info_from_instance(context, instance_ref, network_info, system_metadata, **kw): """Get detailed instance information for an instance which is common to all notifications. :param network_info: network_info provided if not None :param system_metadata: system_metadata DB entries for the instance, if not None. *NOTE*: Currently unused here in trunk, but needed for potential custom modifications. """ def null_safe_str(s): return str(s) if s else '' def null_safe_isotime(s): if isinstance(s, datetime.datetime): return timeutils.strtime(s) else: return str(s) if s else '' image_ref_url = glance.generate_image_url(instance_ref['image_ref']) instance_type = flavors.extract_flavor(instance_ref) instance_type_name = instance_type.get('name', '') if system_metadata is None: system_metadata = utils.instance_sys_meta(instance_ref) instance_info = dict( # Owner properties tenant_id=instance_ref['project_id'], user_id=instance_ref['user_id'], # Identity properties instance_id=instance_ref['uuid'], display_name=instance_ref['display_name'], reservation_id=instance_ref['reservation_id'], hostname=instance_ref['hostname'], # Type properties instance_type=instance_type_name, instance_type_id=instance_ref['instance_type_id'], architecture=instance_ref['architecture'], # Capacity properties memory_mb=instance_ref['memory_mb'], disk_gb=instance_ref['root_gb'] + instance_ref['ephemeral_gb'], vcpus=instance_ref['vcpus'], # Note(dhellmann): This makes the disk_gb value redundant, but # we are keeping it for backwards-compatibility with existing # users of notifications. root_gb=instance_ref['root_gb'], ephemeral_gb=instance_ref['ephemeral_gb'], # Location properties host=instance_ref['host'], node=instance_ref['node'], availability_zone=instance_ref['availability_zone'], # Date properties created_at=str(instance_ref['created_at']), # Nova's deleted vs terminated instance terminology is confusing, # this should be when the instance was deleted (i.e. terminated_at), # not when the db record was deleted. (mdragon) deleted_at=null_safe_isotime(instance_ref.get('terminated_at')), launched_at=null_safe_isotime(instance_ref.get('launched_at')), # Image properties image_ref_url=image_ref_url, os_type=instance_ref['os_type'], kernel_id=instance_ref['kernel_id'], ramdisk_id=instance_ref['ramdisk_id'], # Status properties state=instance_ref['vm_state'], state_description=null_safe_str(instance_ref.get('task_state')), # accessIPs access_ip_v4=instance_ref['access_ip_v4'], access_ip_v6=instance_ref['access_ip_v6'], ) if network_info is not None: fixed_ips = [] for vif in network_info: for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] fixed_ips.append(ip) instance_info['fixed_ips'] = fixed_ips # add image metadata image_meta_props = image_meta(system_metadata) instance_info["image_meta"] = image_meta_props # add instance metadata instance_info['metadata'] = instance_ref['metadata'] instance_info.update(kw) return instance_info
def _do_test_create_volume_backed_image(self, extra_properties): def _fake_id(x): return '%s-%s-%s-%s' % (x * 8, x * 4, x * 4, x * 12) body = dict(createImage=dict(name='snapshot_of_volume_backed')) if extra_properties: body['createImage']['metadata'] = extra_properties image_service = glance.get_default_image_service() bdm = [dict(volume_id=_fake_id('a'), volume_size=1, device_name='vda', delete_on_termination=False)] props = dict(kernel_id=_fake_id('b'), ramdisk_id=_fake_id('c'), root_device_name='/dev/vda', block_device_mapping=bdm) original_image = dict(properties=props, container_format='ami', status='active', is_public=True) image_service.create(None, original_image) def fake_block_device_mapping_get_all_by_instance(context, inst_id, use_slave=False): return [fake_block_device.FakeDbBlockDeviceDict( {'volume_id': _fake_id('a'), 'source_type': 'snapshot', 'destination_type': 'volume', 'volume_size': 1, 'device_name': 'vda', 'snapshot_id': 1, 'boot_index': 0, 'delete_on_termination': False, 'no_device': None})] self.stubs.Set(db, 'block_device_mapping_get_all_by_instance', fake_block_device_mapping_get_all_by_instance) instance = fakes.fake_instance_get(image_ref=original_image['id'], vm_state=vm_states.ACTIVE, root_device_name='/dev/vda') self.stubs.Set(db, 'instance_get_by_uuid', instance) volume = dict(id=_fake_id('a'), size=1, host='fake', display_description='fake') snapshot = dict(id=_fake_id('d')) self.mox.StubOutWithMock(self.controller.compute_api, 'volume_api') volume_api = self.controller.compute_api.volume_api volume_api.get(mox.IgnoreArg(), volume['id']).AndReturn(volume) volume_api.create_snapshot_force(mox.IgnoreArg(), volume['id'], mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(snapshot) self.mox.ReplayAll() req = fakes.HTTPRequestV3.blank(self.url) response = self.controller._action_create_image(req, FAKE_UUID, body) location = response.headers['Location'] image_id = location.replace(glance.generate_image_url(''), '') image = image_service.show(None, image_id) self.assertEqual(image['name'], 'snapshot_of_volume_backed') properties = image['properties'] self.assertEqual(properties['kernel_id'], _fake_id('b')) self.assertEqual(properties['ramdisk_id'], _fake_id('c')) self.assertEqual(properties['root_device_name'], '/dev/vda') self.assertEqual(properties['bdm_v2'], True) bdms = properties['block_device_mapping'] self.assertEqual(len(bdms), 1) self.assertEqual(bdms[0]['boot_index'], 0) self.assertEqual(bdms[0]['source_type'], 'snapshot') self.assertEqual(bdms[0]['destination_type'], 'volume') self.assertEqual(bdms[0]['snapshot_id'], snapshot['id']) for fld in ('connection_info', 'id', 'instance_uuid', 'device_name'): self.assertNotIn(fld, bdms[0]) for k in extra_properties.keys(): self.assertEqual(properties[k], extra_properties[k])
def _do_test_create_volume_backed_image(self, extra_properties): def _fake_id(x): return '%s-%s-%s-%s' % (x * 8, x * 4, x * 4, x * 12) body = dict(create_image=dict(name='snapshot_of_volume_backed')) if extra_properties: body['create_image']['metadata'] = extra_properties image_service = glance.get_default_image_service() bdm = [ dict(volume_id=_fake_id('a'), volume_size=1, device_name='vda', delete_on_termination=False) ] props = dict(kernel_id=_fake_id('b'), ramdisk_id=_fake_id('c'), root_device_name='/dev/vda', block_device_mapping=bdm) original_image = dict(properties=props, container_format='ami', status='active', is_public=True) image_service.create(None, original_image) def fake_block_device_mapping_get_all_by_instance(context, inst_id): return [ dict(volume_id=_fake_id('a'), source_type='snapshot', destination_type='volume', volume_size=1, device_name='vda', snapshot_id=1, boot_index=0, delete_on_termination=False, no_device=None) ] self.stubs.Set(db, 'block_device_mapping_get_all_by_instance', fake_block_device_mapping_get_all_by_instance) instance = fakes.fake_instance_get(image_ref=original_image['id'], vm_state=vm_states.ACTIVE, root_device_name='/dev/vda') self.stubs.Set(db, 'instance_get_by_uuid', instance) volume = dict(id=_fake_id('a'), size=1, host='fake', display_description='fake') snapshot = dict(id=_fake_id('d')) self.mox.StubOutWithMock(self.controller.compute_api, 'volume_api') volume_api = self.controller.compute_api.volume_api volume_api.get(mox.IgnoreArg(), volume['id']).AndReturn(volume) volume_api.create_snapshot_force(mox.IgnoreArg(), volume['id'], mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(snapshot) self.mox.ReplayAll() req = fakes.HTTPRequestV3.blank(self.url) response = self.controller._action_create_image(req, FAKE_UUID, body) location = response.headers['Location'] image_id = location.replace(glance.generate_image_url(''), '') image = image_service.show(None, image_id) self.assertEqual(image['name'], 'snapshot_of_volume_backed') properties = image['properties'] self.assertEqual(properties['kernel_id'], _fake_id('b')) self.assertEqual(properties['ramdisk_id'], _fake_id('c')) self.assertEqual(properties['root_device_name'], '/dev/vda') bdms = properties['block_device_mapping'] self.assertEqual(len(bdms), 1) self.assertEqual(bdms[0]['device_name'], 'vda') self.assertEqual(bdms[0]['snapshot_id'], snapshot['id']) for k in extra_properties.keys(): self.assertEqual(properties[k], extra_properties[k])
def _do_test_create_volume_backed_image(self, extra_properties): def _fake_id(x): return "%s-%s-%s-%s" % (x * 8, x * 4, x * 4, x * 12) body = dict(createImage=dict(name="snapshot_of_volume_backed")) if extra_properties: body["createImage"]["metadata"] = extra_properties image_service = glance.get_default_image_service() bdm = [dict(volume_id=_fake_id("a"), volume_size=1, device_name="vda", delete_on_termination=False)] def fake_block_device_mapping_get_all_by_instance(context, inst_id, use_slave=False): return [ fake_block_device.FakeDbBlockDeviceDict( { "volume_id": _fake_id("a"), "source_type": "snapshot", "destination_type": "volume", "volume_size": 1, "device_name": "vda", "snapshot_id": 1, "boot_index": 0, "delete_on_termination": False, "no_device": None, } ) ] self.stub_out("nova.db.block_device_mapping_get_all_by_instance", fake_block_device_mapping_get_all_by_instance) system_metadata = dict( image_kernel_id=_fake_id("b"), image_ramdisk_id=_fake_id("c"), image_root_device_name="/dev/vda", image_block_device_mapping=str(bdm), image_container_format="ami", ) instance = fakes.fake_instance_get( image_ref=str(uuid.uuid4()), vm_state=vm_states.ACTIVE, root_device_name="/dev/vda", system_metadata=system_metadata, ) self.stub_out("nova.db.instance_get_by_uuid", instance) self.mox.StubOutWithMock(self.controller.compute_api.compute_rpcapi, "quiesce_instance") self.controller.compute_api.compute_rpcapi.quiesce_instance(mox.IgnoreArg(), mox.IgnoreArg()).AndRaise( exception.InstanceQuiesceNotSupported(instance_id="fake", reason="test") ) volume = dict(id=_fake_id("a"), size=1, host="fake", display_description="fake") snapshot = dict(id=_fake_id("d")) self.mox.StubOutWithMock(self.controller.compute_api, "volume_api") volume_api = self.controller.compute_api.volume_api volume_api.get(mox.IgnoreArg(), volume["id"]).AndReturn(volume) volume_api.create_snapshot_force(mox.IgnoreArg(), volume["id"], mox.IgnoreArg(), mox.IgnoreArg()).AndReturn( snapshot ) self.mox.ReplayAll() response = self.controller._action_create_image(self.req, FAKE_UUID, body=body) location = response.headers["Location"] image_id = location.replace(self.image_url or glance.generate_image_url(""), "") image = image_service.show(None, image_id) self.assertEqual(image["name"], "snapshot_of_volume_backed") properties = image["properties"] self.assertEqual(properties["kernel_id"], _fake_id("b")) self.assertEqual(properties["ramdisk_id"], _fake_id("c")) self.assertEqual(properties["root_device_name"], "/dev/vda") self.assertTrue(properties["bdm_v2"]) bdms = properties["block_device_mapping"] self.assertEqual(len(bdms), 1) self.assertEqual(bdms[0]["boot_index"], 0) self.assertEqual(bdms[0]["source_type"], "snapshot") self.assertEqual(bdms[0]["destination_type"], "volume") self.assertEqual(bdms[0]["snapshot_id"], snapshot["id"]) self.assertEqual("/dev/vda", bdms[0]["device_name"]) for fld in ("connection_info", "id", "instance_uuid"): self.assertNotIn(fld, bdms[0]) for k in extra_properties.keys(): self.assertEqual(properties[k], extra_properties[k])
def info_from_instance(context, instance_ref, network_info, system_metadata, **kw): """Get detailed instance information for an instance which is common to all notifications. :param:network_info: network_info provided if not None :param:system_metadata: system_metadata DB entries for the instance, if not None .. note:: Currently unused here in trunk, but needed for potential custom modifications. """ def null_safe_str(s): return str(s) if s else '' def null_safe_isotime(s): if isinstance(s, datetime.datetime): return timeutils.strtime(s) else: return str(s) if s else '' image_ref_url = glance.generate_image_url(instance_ref['image_ref']) instance_type = flavors.extract_flavor(instance_ref) instance_type_name = instance_type.get('name', '') instance_flavorid = instance_type.get('flavorid', '') if system_metadata is None: system_metadata = utils.instance_sys_meta(instance_ref) instance_info = dict( # Owner properties tenant_id=instance_ref['project_id'], user_id=instance_ref['user_id'], # Identity properties instance_id=instance_ref['uuid'], display_name=instance_ref['display_name'], reservation_id=instance_ref['reservation_id'], hostname=instance_ref['hostname'], # Type properties instance_type=instance_type_name, instance_type_id=instance_ref['instance_type_id'], instance_flavor_id=instance_flavorid, architecture=instance_ref['architecture'], # Capacity properties memory_mb=instance_ref['memory_mb'], disk_gb=instance_ref['root_gb'] + instance_ref['ephemeral_gb'], vcpus=instance_ref['vcpus'], # Note(dhellmann): This makes the disk_gb value redundant, but # we are keeping it for backwards-compatibility with existing # users of notifications. root_gb=instance_ref['root_gb'], ephemeral_gb=instance_ref['ephemeral_gb'], # Location properties host=instance_ref['host'], node=instance_ref['node'], availability_zone=instance_ref['availability_zone'], # Date properties created_at=str(instance_ref['created_at']), # Terminated and Deleted are slightly different (although being # terminated and not deleted is a transient state), so include # both and let the recipient decide which they want to use. terminated_at=null_safe_isotime(instance_ref.get('terminated_at')), deleted_at=null_safe_isotime(instance_ref.get('deleted_at')), launched_at=null_safe_isotime(instance_ref.get('launched_at')), # Image properties image_ref_url=image_ref_url, os_type=instance_ref['os_type'], kernel_id=instance_ref['kernel_id'], ramdisk_id=instance_ref['ramdisk_id'], # Status properties state=instance_ref['vm_state'], state_description=null_safe_str(instance_ref.get('task_state')), # accessIPs access_ip_v4=instance_ref['access_ip_v4'], access_ip_v6=instance_ref['access_ip_v6'], ) if network_info is not None: fixed_ips = [] for vif in network_info: for ip in vif.fixed_ips(): ip["label"] = vif["network"]["label"] ip["vif_mac"] = vif["address"] fixed_ips.append(ip) instance_info['fixed_ips'] = fixed_ips # add image metadata image_meta_props = image_meta(system_metadata) instance_info["image_meta"] = image_meta_props # add instance metadata instance_info['metadata'] = utils.instance_meta(instance_ref) instance_info.update(kw) return instance_info
def _do_test_create_volume_backed_image(self, extra_properties): def _fake_id(x): return "%s-%s-%s-%s" % (x * 8, x * 4, x * 4, x * 12) body = dict(create_image=dict(name="snapshot_of_volume_backed")) if extra_properties: body["create_image"]["metadata"] = extra_properties image_service = glance.get_default_image_service() bdm = [dict(volume_id=_fake_id("a"), volume_size=1, device_name="vda", delete_on_termination=False)] props = dict( kernel_id=_fake_id("b"), ramdisk_id=_fake_id("c"), root_device_name="/dev/vda", block_device_mapping=bdm ) original_image = dict(properties=props, container_format="ami", status="active", is_public=True) image_service.create(None, original_image) def fake_block_device_mapping_get_all_by_instance(context, inst_id, use_slave=False): return [ fake_block_device.FakeDbBlockDeviceDict( { "volume_id": _fake_id("a"), "source_type": "snapshot", "destination_type": "volume", "volume_size": 1, "device_name": "vda", "snapshot_id": 1, "boot_index": 0, "delete_on_termination": False, "no_device": None, } ) ] self.stubs.Set(db, "block_device_mapping_get_all_by_instance", fake_block_device_mapping_get_all_by_instance) instance = fakes.fake_instance_get( image_ref=original_image["id"], vm_state=vm_states.ACTIVE, root_device_name="/dev/vda" ) self.stubs.Set(db, "instance_get_by_uuid", instance) volume = dict(id=_fake_id("a"), size=1, host="fake", display_description="fake") snapshot = dict(id=_fake_id("d")) self.mox.StubOutWithMock(self.controller.compute_api, "volume_api") volume_api = self.controller.compute_api.volume_api volume_api.get(mox.IgnoreArg(), volume["id"]).AndReturn(volume) volume_api.create_snapshot_force(mox.IgnoreArg(), volume["id"], mox.IgnoreArg(), mox.IgnoreArg()).AndReturn( snapshot ) self.mox.ReplayAll() req = fakes.HTTPRequestV3.blank(self.url) response = self.controller._action_create_image(req, FAKE_UUID, body) location = response.headers["Location"] image_id = location.replace(glance.generate_image_url(""), "") image = image_service.show(None, image_id) self.assertEqual(image["name"], "snapshot_of_volume_backed") properties = image["properties"] self.assertEqual(properties["kernel_id"], _fake_id("b")) self.assertEqual(properties["ramdisk_id"], _fake_id("c")) self.assertEqual(properties["root_device_name"], "/dev/vda") self.assertEqual(properties["bdm_v2"], True) bdms = properties["block_device_mapping"] self.assertEqual(len(bdms), 1) self.assertEqual(bdms[0]["boot_index"], 0) self.assertEqual(bdms[0]["source_type"], "snapshot") self.assertEqual(bdms[0]["destination_type"], "volume") self.assertEqual(bdms[0]["snapshot_id"], snapshot["id"]) for fld in ("connection_info", "id", "instance_uuid", "device_name"): self.assertNotIn(fld, bdms[0]) for k in extra_properties.keys(): self.assertEqual(properties[k], extra_properties[k])