Skip to content

Commit

Permalink
Merge "Boot from volume without image supplied"
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenkins authored and openstack-gerrit committed Dec 5, 2012
2 parents f354276 + 1d00dfc commit 255692f
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 129 deletions.
23 changes: 20 additions & 3 deletions nova/api/openstack/compute/servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,8 +743,7 @@ def create(self, req, body):
self._validate_server_name(name)
name = name.strip()

image_href = self._image_ref_from_req_data(body)
image_href = self._image_uuid_from_href(image_href)
image_uuid = self._image_from_req_data(body)

personality = server_dict.get('personality')
config_drive = None
Expand Down Expand Up @@ -855,7 +854,7 @@ def create(self, req, body):

(instances, resv_id) = self.compute_api.create(context,
inst_type,
image_href,
image_uuid,
display_name=name,
display_description=name,
key_name=key_name,
Expand Down Expand Up @@ -1108,6 +1107,24 @@ def _image_uuid_from_href(self, image_href):

return image_uuid

def _image_from_req_data(self, data):
"""
Get image data from the request or raise appropriate
exceptions
If no image is supplied - checks to see if there is
block devices set and proper extesions loaded.
"""
image_ref = data['server'].get('imageRef')
bdm = data['server'].get('block_device_mapping')

if not image_ref and bdm and self.ext_mgr.is_loaded('os-volumes'):
return ''
else:
image_href = self._image_ref_from_req_data(data)
image_uuid = self._image_uuid_from_href(image_href)
return image_uuid

def _flavor_id_from_req_data(self, data):
try:
flavor_ref = data['server']['flavorRef']
Expand Down
25 changes: 14 additions & 11 deletions nova/api/openstack/compute/views/servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,17 +164,20 @@ def _get_addresses(self, request, instance):

def _get_image(self, request, instance):
image_ref = instance["image_ref"]
image_id = str(common.get_id_from_href(image_ref))
bookmark = self._image_builder._get_bookmark_link(request,
image_id,
"images")
return {
"id": image_id,
"links": [{
"rel": "bookmark",
"href": bookmark,
}],
}
if image_ref:
image_id = str(common.get_id_from_href(image_ref))
bookmark = self._image_builder._get_bookmark_link(request,
image_id,
"images")
return {
"id": image_id,
"links": [{
"rel": "bookmark",
"href": bookmark,
}],
}
else:
return ""

def _get_flavor(self, request, instance):
instance_type = instance["instance_type"]
Expand Down
82 changes: 50 additions & 32 deletions nova/compute/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,7 @@ def _check_requested_networks(self, context, requested_networks):
self.network_api.validate_networks(context, requested_networks)

@staticmethod
def _handle_kernel_and_ramdisk(context, kernel_id, ramdisk_id, image,
image_service):
def _handle_kernel_and_ramdisk(context, kernel_id, ramdisk_id, image):
"""Choose kernel and ramdisk appropriate for the instance.
The kernel and ramdisk can be chosen in one of three ways:
Expand All @@ -330,11 +329,13 @@ def _handle_kernel_and_ramdisk(context, kernel_id, ramdisk_id, image,
3. Forced to None by using `null_kernel` FLAG.
"""
# Inherit from image if not specified
image_properties = image.get('properties', {})

if kernel_id is None:
kernel_id = image['properties'].get('kernel_id')
kernel_id = image_properties.get('kernel_id')

if ramdisk_id is None:
ramdisk_id = image['properties'].get('ramdisk_id')
ramdisk_id = image_properties.get('ramdisk_id')

# Force to None if using null_kernel
if kernel_id == str(CONF.null_kernel):
Expand All @@ -343,9 +344,13 @@ def _handle_kernel_and_ramdisk(context, kernel_id, ramdisk_id, image,

# Verify kernel and ramdisk exist (fail-fast)
if kernel_id is not None:
image_service, kernel_id = glance.get_remote_image_service(
context, kernel_id)
image_service.show(context, kernel_id)

if ramdisk_id is not None:
image_service, ramdisk_id = glance.get_remote_image_service(
context, ramdisk_id)
image_service.show(context, ramdisk_id)

return kernel_id, ramdisk_id
Expand All @@ -367,9 +372,11 @@ def _handle_availability_zone(availability_zone):

@staticmethod
def _inherit_properties_from_image(image, auto_disk_config):
image_properties = image.get('properties', {})

def prop(prop_, prop_type=None):
"""Return the value of an image property."""
value = image['properties'].get(prop_)
value = image_properties.get(prop_)

if value is not None:
if prop_type == 'bool':
Expand Down Expand Up @@ -435,17 +442,23 @@ def _create_instance(self, context, instance_type,
self._check_injected_file_quota(context, injected_files)
self._check_requested_networks(context, requested_networks)

(image_service, image_id) = glance.get_remote_image_service(
context, image_href)
image = image_service.show(context, image_id)
if image['status'] != 'active':
raise exception.ImageNotActive(image_id=image_id)
if image_href:
(image_service, image_id) = glance.get_remote_image_service(
context, image_href)
image = image_service.show(context, image_id)
if image['status'] != 'active':
raise exception.ImageNotActive(image_id=image_id)
else:
image = {}

if instance_type['memory_mb'] < int(image.get('min_ram') or 0):
raise exception.InstanceTypeMemoryTooSmall()
if instance_type['root_gb'] < int(image.get('min_disk') or 0):
raise exception.InstanceTypeDiskTooSmall()

kernel_id, ramdisk_id = self._handle_kernel_and_ramdisk(
context, kernel_id, ramdisk_id, image)

# Handle config_drive
config_drive_id = None
if config_drive and config_drive is not True:
Expand All @@ -454,10 +467,9 @@ def _create_instance(self, context, instance_type,
config_drive = None

# Ensure config_drive image exists
image_service.show(context, config_drive_id)

kernel_id, ramdisk_id = self._handle_kernel_and_ramdisk(
context, kernel_id, ramdisk_id, image, image_service)
cd_image_service, config_drive_id = \
glance.get_remote_image_service(context, config_drive_id)
cd_image_service.show(context, config_drive_id)

if key_data is None and key_name:
key_pair = self.db.key_pair_get(context, context.user_id,
Expand All @@ -467,11 +479,8 @@ def _create_instance(self, context, instance_type,
if reservation_id is None:
reservation_id = utils.generate_uid('r')

# grab the architecture from glance
architecture = image['properties'].get('architecture', 'Unknown')

root_device_name = block_device.properties_root_device_name(
image['properties'])
image.get('properties', {}))

availability_zone, forced_host = self._handle_availability_zone(
availability_zone)
Expand Down Expand Up @@ -505,7 +514,6 @@ def _create_instance(self, context, instance_type,
'access_ip_v6': access_ip_v6,
'availability_zone': availability_zone,
'root_device_name': root_device_name,
'architecture': architecture,
'progress': 0}

if user_data:
Expand Down Expand Up @@ -663,12 +671,13 @@ def _populate_instance_for_bdm(self, context, instance, instance_type,
# require elevated context?
elevated = context.elevated()
instance_uuid = instance['uuid']
mappings = image['properties'].get('mappings', [])
image_properties = image.get('properties', {})
mappings = image_properties.get('mappings', [])
if mappings:
self._update_image_block_device_mapping(elevated,
instance_type, instance_uuid, mappings)

image_bdm = image['properties'].get('block_device_mapping', [])
image_bdm = image_properties.get('block_device_mapping', [])
for mapping in (image_bdm, block_device_mapping):
if not mapping:
continue
Expand All @@ -678,9 +687,10 @@ def _populate_instance_for_bdm(self, context, instance, instance_type,
def _populate_instance_shutdown_terminate(self, instance, image,
block_device_mapping):
"""Populate instance shutdown_terminate information."""
image_properties = image.get('properties', {})
if (block_device_mapping or
image['properties'].get('mappings') or
image['properties'].get('block_device_mapping')):
image_properties.get('mappings') or
image_properties.get('block_device_mapping')):
instance['shutdown_terminate'] = False

def _populate_instance_names(self, instance):
Expand All @@ -701,6 +711,7 @@ def _default_display_name(self, instance_uuid):
def _populate_instance_for_create(self, base_options, image,
security_groups):
"""Build the beginning of a new instance."""
image_properties = image.get('properties', {})

instance = base_options
if not instance.get('uuid'):
Expand All @@ -716,13 +727,13 @@ def _populate_instance_for_create(self, base_options, image,
# Store image properties so we can use them later
# (for notifications, etc). Only store what we can.
instance.setdefault('system_metadata', {})
for key, value in image['properties'].iteritems():
for key, value in image_properties.iteritems():
new_value = str(value)[:255]
instance['system_metadata']['image_%s' % key] = new_value

# Keep a record of the original base image that this
# image's instance is derived from:
base_image_ref = image['properties'].get('base_image_ref')
base_image_ref = image_properties.get('base_image_ref')
if not base_image_ref:
# base image ref property not previously set through a snapshot.
# default to using the image ref as the base:
Expand Down Expand Up @@ -1509,8 +1520,12 @@ def _get_image(self, context, image_href):
def rebuild(self, context, instance, image_href, admin_password, **kwargs):
"""Rebuild the given instance with the provided attributes."""

orig_image_ref = instance['image_ref']
image = self._get_image(context, image_href)
if instance['image_ref']:
orig_image_ref = instance['image_ref']
image = self._get_image(context, image_href)
else:
orig_image_ref = ''
image = {}

files_to_inject = kwargs.pop('files_to_inject', [])
self._check_injected_file_quota(context, files_to_inject)
Expand All @@ -1524,11 +1539,14 @@ def rebuild(self, context, instance, image_href, admin_password, **kwargs):
if instance_type['root_gb'] < int(image.get('min_disk') or 0):
raise exception.InstanceTypeDiskTooSmall()

(image_service, image_id) = glance.get_remote_image_service(context,
image_href)
image = image_service.show(context, image_id)
if image_href:
(image_service, image_id) = glance.get_remote_image_service(
context, image_href)
image = image_service.show(context, image_id)
else:
image = {}
kernel_id, ramdisk_id = self._handle_kernel_and_ramdisk(
context, None, None, image, image_service)
context, None, None, image)

def _reset_image_metadata():
"""
Expand All @@ -1551,7 +1569,7 @@ def _reset_image_metadata():
if key.startswith('image_'):
del sys_metadata[key]
# Add the new ones
for key, value in image['properties'].iteritems():
for key, value in image.get('properties', {}).iteritems():
new_value = str(value)[:255]
sys_metadata['image_%s' % key] = new_value
self.db.instance_system_metadata_update(context,
Expand Down
26 changes: 19 additions & 7 deletions nova/compute/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,11 @@ def _run_instance(self, context, request_spec,
LOG.debug(_("No node specified, defaulting to %(node)s") %
locals())

extra_usage_info = {"image_name": image_meta['name']}
if image_meta:
extra_usage_info = {"image_name": image_meta['name']}
else:
extra_usage_info = {}

self._start_building(context, instance)
self._notify_about_instance_usage(
context, instance, "create.start",
Expand Down Expand Up @@ -770,7 +774,10 @@ def _check_image_size(self, context, instance):
image, but is accurate because it reflects the image's
actual size.
"""
image_meta = _get_image_meta(context, instance['image_ref'])
if instance['image_ref']:
image_meta = _get_image_meta(context, instance['image_ref'])
else: # Instance was started from volume - so no image ref
return {}

try:
size_bytes = image_meta['size']
Expand Down Expand Up @@ -1205,7 +1212,10 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
LOG.audit(_("Rebuilding instance"), context=context,
instance=instance)

image_meta = _get_image_meta(context, image_ref)
if image_ref:
image_meta = _get_image_meta(context, image_ref)
else:
image_meta = {}

# This instance.exists message should contain the original
# image_ref, not the new one. Since the DB has been updated
Expand All @@ -1217,7 +1227,7 @@ def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
extra_usage_info=extra_usage_info)

# This message should contain the new image_ref
extra_usage_info = {'image_name': image_meta['name']}
extra_usage_info = {'image_name': image_meta.get('name', '')}
self._notify_about_instance_usage(context, instance,
"rebuild.start", extra_usage_info=extra_usage_info)

Expand Down Expand Up @@ -1558,10 +1568,12 @@ def rescue_instance(self, context, instance, rescue_password=None):

network_info = self._get_instance_nw_info(context, instance)

# Boot the instance using the 'base' image instead of the user's
# current (possibly broken) image
rescue_image_ref = self._get_rescue_image_ref(context, instance)
rescue_image_meta = _get_image_meta(context, rescue_image_ref)

if rescue_image_ref:
rescue_image_meta = _get_image_meta(context, rescue_image_ref)
else:
rescue_image_meta = {}

with self._error_out_instance_on_exception(context, instance['uuid']):
self.driver.rescue(context, instance,
Expand Down

0 comments on commit 255692f

Please sign in to comment.