Merge "Add an extra property for selecting SCG / Storage Template"
This commit is contained in:
commit
ab21eb0a57
|
@ -503,6 +503,12 @@ class Utils(object):
|
|||
|
||||
return accessible_storage_providers
|
||||
|
||||
def get_all_storage_templates(self):
|
||||
"""
|
||||
Get all storage templates from PowerVC
|
||||
"""
|
||||
return self._cinderclient.volume_types.list_all_storage_templates()
|
||||
|
||||
def get_multi_scg_accessible_storage_templates(self,
|
||||
scg_uuid_list,
|
||||
scg_name_list):
|
||||
|
@ -587,6 +593,37 @@ class Utils(object):
|
|||
(accessible_storage_templates)))
|
||||
return accessible_storage_templates
|
||||
|
||||
def get_scg_accessible_storage_templates_extended(self,
|
||||
scg_list,
|
||||
all_storage_templates):
|
||||
|
||||
"""
|
||||
Get a dict that stores the scgs and the corresponding
|
||||
accessible storage templates . The key is scg id ,
|
||||
and the value is the storage templates.
|
||||
|
||||
:param: scg_list : All available Storage Connectivity Groups
|
||||
:param: all_storage_templates : All available storage
|
||||
templates from PowerVC
|
||||
:return: The dict to store the scgs and
|
||||
the corresponding storage templates
|
||||
"""
|
||||
if scg_list is not None:
|
||||
scg_storage_templates = {}
|
||||
for scg in scg_list:
|
||||
accessible_storage_templates = []
|
||||
volume_types = scg.list_all_volume_types()
|
||||
volume_type_ids = []
|
||||
for vol_type in volume_types:
|
||||
volume_type_ids.append(vol_type.__dict__.get("id"))
|
||||
for storage_template in all_storage_templates:
|
||||
if(storage_template.__dict__.get("id") in volume_type_ids):
|
||||
accessible_storage_templates.append(storage_template)
|
||||
scg_storage_templates[scg.id] = accessible_storage_templates
|
||||
return scg_storage_templates
|
||||
else:
|
||||
return {}
|
||||
|
||||
def get_multi_scg_accessible_volumes(self,
|
||||
scg_uuid_list,
|
||||
scg_name_list,
|
||||
|
@ -770,6 +807,30 @@ class Utils(object):
|
|||
LOG.debug(_('Unable to find staging user: %s'), staginguser)
|
||||
raise exception.StagingUserNotFound(name=staginguser)
|
||||
|
||||
def get_image_scgs_dict(self, scg_list):
|
||||
"""
|
||||
Get a dict that store the image and the corresponding scg list .
|
||||
The key is the image UUID , and the value is the corresponding scg list
|
||||
|
||||
:param scg_list : All available Storage Connectivity Groups
|
||||
:returns The dict to store the image and the corresponding scg list
|
||||
"""
|
||||
if scg_list is not None:
|
||||
image_scgs_dict = {}
|
||||
for scg in scg_list:
|
||||
image_id_list = self.get_scg_image_ids(scg.id)
|
||||
for image_id in image_id_list:
|
||||
if image_id in image_scgs_dict.keys():
|
||||
scg_list = image_scgs_dict[image_id]
|
||||
scg_list.append(scg)
|
||||
image_scgs_dict[image_id] = scg_list
|
||||
else:
|
||||
scg_list = [scg]
|
||||
image_scgs_dict[image_id] = scg_list
|
||||
return image_scgs_dict
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
def import_relative_module(relative_import_str, import_str):
|
||||
"""
|
||||
|
|
|
@ -17,6 +17,7 @@ from powervc.common import config
|
|||
from nova.openstack.common import service
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import timeutils
|
||||
from nova.openstack.common import jsonutils
|
||||
from glanceclient.v1 import images as v1images
|
||||
from glanceclient.exc import CommunicationError
|
||||
from glanceclient.exc import HTTPNotFound
|
||||
|
@ -231,6 +232,11 @@ class PowerVCImageManager(service.Service):
|
|||
local_v2client = self._get_local_v2_client()
|
||||
v2local_images = local_v2client.images
|
||||
|
||||
# Get image_scg_dict and scg_storage_templates_dict for
|
||||
# the special property image_topology format
|
||||
image_scgs_dict, scg_storage_templates_dict = \
|
||||
self._get_dicts_for_extra_image_property()
|
||||
|
||||
# When catching exceptions during sync operations we will look for
|
||||
# CommunicationError, and raise those so we don't waste time
|
||||
# trying to process all images when there is a connection failure.
|
||||
|
@ -277,6 +283,15 @@ class PowerVCImageManager(service.Service):
|
|||
if uuid in self.ids_dict.keys():
|
||||
self.ids_dict.pop(uuid)
|
||||
else:
|
||||
# Add an extra property named image_topology , which allows
|
||||
# user to select a SCG/Storage Template when booting an VM
|
||||
pvc_image = pvc_image_dict[uuid]
|
||||
if pvc_image:
|
||||
pvc_image = \
|
||||
self._insert_extra_property_image_topology(
|
||||
pvc_image,
|
||||
image_scgs_dict,
|
||||
scg_storage_templates_dict)
|
||||
|
||||
# Update the image if it has changed. (Right now, always
|
||||
# update it, and update all fields). Update using the
|
||||
|
@ -326,6 +341,14 @@ class PowerVCImageManager(service.Service):
|
|||
# that are accessible from our Storage Connectivity
|
||||
# Group
|
||||
if status and status == 'active':
|
||||
# Add an extra property named image_topology ,
|
||||
# which allows user to select a SCG/Storage
|
||||
# Template when booting an VM
|
||||
pvc_image = \
|
||||
self._insert_extra_property_image_topology(
|
||||
pvc_image,
|
||||
image_scgs_dict,
|
||||
scg_storage_templates_dict)
|
||||
|
||||
# Add or activate the local image
|
||||
self._add_or_activate_local_image(
|
||||
|
@ -463,6 +486,11 @@ class PowerVCImageManager(service.Service):
|
|||
cur_local_image_set = set(local_image_dict)
|
||||
cur_pvc_image_set = set(pvc_image_dict)
|
||||
|
||||
# Get image_scg_dict and scg_storage_templates_dict for
|
||||
# the special property image_topology format
|
||||
image_scgs_dict, scg_storage_templates_dict = \
|
||||
self._get_dicts_for_extra_image_property()
|
||||
|
||||
# We only need to update sync images that are in both PowerVC and
|
||||
# the local hosting OS. If an image is missing from either side
|
||||
# it will be added or deleted, so no need to try to update it.
|
||||
|
@ -484,6 +512,21 @@ class PowerVCImageManager(service.Service):
|
|||
pvc_image = pvc_image_dict[uuid]
|
||||
local_updated = self._local_image_updated(uuid, local_image)
|
||||
pvc_updated = self._pvc_image_updated(uuid, pvc_image)
|
||||
|
||||
# Add an extra property named image_topology , which allows
|
||||
# user to select a SCG/Storage Template when booting an VM
|
||||
pvc_image = \
|
||||
self._insert_extra_property_image_topology(
|
||||
pvc_image,
|
||||
image_scgs_dict,
|
||||
scg_storage_templates_dict)
|
||||
|
||||
if 'image_topology' not in local_image.properties or \
|
||||
local_image.properties['image_topology'] != \
|
||||
pvc_image.properties['image_topology']:
|
||||
if not pvc_updated:
|
||||
pvc_updated = True
|
||||
|
||||
local_checksum = \
|
||||
self._get_image_checksum(local_image.to_dict())
|
||||
pvc_checksum = self._get_image_checksum(pvc_image.to_dict())
|
||||
|
@ -543,11 +586,18 @@ class PowerVCImageManager(service.Service):
|
|||
'the local hosting OS to PowerVC'),
|
||||
local_image.name)
|
||||
|
||||
# To avoid the image property image_topology is
|
||||
# synced to PowerVC side
|
||||
local_image = \
|
||||
self._filter_out_image_properties(local_image,
|
||||
['image_topology'])
|
||||
|
||||
# Update sync local image to PowerVC
|
||||
updated_image = self._update_pvc_image(uuid, local_image,
|
||||
pvc_image,
|
||||
v1pvc_images,
|
||||
v2pvc_images)
|
||||
|
||||
if updated_image is None:
|
||||
LOG.error(_('PowerVC image \'%s\' with UUID %s was not'
|
||||
' updated during periodic image '
|
||||
|
@ -767,6 +817,15 @@ class PowerVCImageManager(service.Service):
|
|||
# that are accessible on our Storage Connectivity Group
|
||||
if status and status == 'active':
|
||||
|
||||
# Add an extra property named image_topology ,
|
||||
# which allows user to select a SCG/Storage
|
||||
# Template when booting an VM
|
||||
pvc_image = \
|
||||
self._insert_extra_property_image_topology(
|
||||
pvc_image,
|
||||
image_scgs_dict,
|
||||
scg_storage_templates_dict)
|
||||
|
||||
# Add or activate the local image
|
||||
self._add_or_activate_local_image(
|
||||
pvc_image, local_image_owner,
|
||||
|
@ -1058,6 +1117,12 @@ class PowerVCImageManager(service.Service):
|
|||
'merged master image to PowerVC for PowerVC UUID '
|
||||
'%s'), master_image.name, uuid)
|
||||
|
||||
# To avoid the image property image_topology is
|
||||
# synced to PowerVC side
|
||||
master_image = \
|
||||
self._filter_out_image_properties(master_image,
|
||||
['image_topology'])
|
||||
|
||||
# Update sync master image to PowerVC
|
||||
LOG.debug(_('Master image for pvc: %s'), str(master_image))
|
||||
updated_pvc_image = self._update_pvc_image(uuid, master_image,
|
||||
|
@ -1238,6 +1303,7 @@ class PowerVCImageManager(service.Service):
|
|||
|
||||
# Reset the image properties
|
||||
master_image.properties = master_props
|
||||
master_image._info['properties'] = master_props
|
||||
LOG.debug(_('Master image for merge: %s'), str(master_image))
|
||||
|
||||
def _get_image(self, uuid, image_id, image_name, v1images, v2images):
|
||||
|
@ -2583,6 +2649,12 @@ class PowerVCImageManager(service.Service):
|
|||
pvc_image.name, pvc_id)
|
||||
return
|
||||
|
||||
# To avoid the image property image_topology is
|
||||
# synced to PowerVC side
|
||||
local_image = \
|
||||
self._filter_out_image_properties(local_image,
|
||||
['image_topology'])
|
||||
|
||||
# Perform the image update to PowerVC
|
||||
image = self._update_pvc_image(pvc_id, local_image, pvc_image,
|
||||
v1pvc_images, v2pvc_images)
|
||||
|
@ -3719,6 +3791,109 @@ class PowerVCImageManager(service.Service):
|
|||
self._unescape(filtered_props)
|
||||
return filtered_props
|
||||
|
||||
def _get_extra_property_image_topology(self,
|
||||
imageUUID,
|
||||
image_scg_dict,
|
||||
scg_storage_template_dict):
|
||||
"""
|
||||
Get an extra image property , named "image_topology" ,
|
||||
which is used UI to select an available Storage
|
||||
Connectivity Groups or Storage templates.
|
||||
"""
|
||||
if imageUUID is not None:
|
||||
image_topology = []
|
||||
scg_list = image_scg_dict[imageUUID]
|
||||
for scg in scg_list:
|
||||
scg_topology = {}
|
||||
scg_topology['scg_id'] = scg.id
|
||||
scg_topology['display_name'] = scg.display_name
|
||||
|
||||
scg_storage_templates = scg_storage_template_dict[scg.id]
|
||||
available_storage_templates = []
|
||||
for storage_template in scg_storage_templates:
|
||||
storage_template_dict = {}
|
||||
storage_template_dict['id'] = storage_template.id
|
||||
storage_template_dict['name'] = storage_template.name
|
||||
available_storage_templates.append(storage_template_dict)
|
||||
if available_storage_templates:
|
||||
scg_topology['storage_template_list'] = \
|
||||
available_storage_templates
|
||||
image_topology.append(scg_topology)
|
||||
|
||||
json_image_topology = jsonutils.dumps(image_topology)
|
||||
return json_image_topology
|
||||
else:
|
||||
return []
|
||||
|
||||
def _insert_extra_property_image_topology(self,
|
||||
image,
|
||||
image_scg_dict,
|
||||
scg_storage_template_dict):
|
||||
"""
|
||||
Insert or Update the extra property "image_topology" for
|
||||
the image object and return the image object.
|
||||
|
||||
:param: image The image object that need to be updated
|
||||
:param: image_scg_dict: A dict to store image UUID and
|
||||
the corresponding scg list
|
||||
:param: scg_storage_template_dict: A dict to store scg UUID and
|
||||
the corresponding storage template list
|
||||
:return The image object with the property "image_topology" updated
|
||||
"""
|
||||
if image is not None and \
|
||||
image_scg_dict is not None and \
|
||||
scg_storage_template_dict:
|
||||
|
||||
image_topology_prop = \
|
||||
self._get_extra_property_image_topology(
|
||||
image.id,
|
||||
image_scg_dict,
|
||||
scg_storage_template_dict)
|
||||
image_properties = self._get_image_properties(image.to_dict())
|
||||
image_properties[u'image_topology'] = unicode(image_topology_prop)
|
||||
image.properties = image_properties
|
||||
image._info['properties'] = image_properties
|
||||
return image
|
||||
else:
|
||||
return image
|
||||
|
||||
def _filter_out_image_properties(self, image, props):
|
||||
"""
|
||||
Delete the properties in the props list for the image object and return
|
||||
the image object without these properties.
|
||||
|
||||
:param: image The image object that need to be filtered out
|
||||
:param: props The properties need to delete
|
||||
:return The image object without these specific properties in the props
|
||||
"""
|
||||
if image is not None and props is not None:
|
||||
image_properties = self._get_image_properties(image.to_dict())
|
||||
for prop in props:
|
||||
if prop in image_properties.keys():
|
||||
del(image_properties[prop])
|
||||
image.properties = image_properties
|
||||
image._info['properties'] = image_properties
|
||||
return image
|
||||
else:
|
||||
return image
|
||||
|
||||
def _get_dicts_for_extra_image_property(self):
|
||||
"""
|
||||
Get two dict to format the extra property "image_topology" , one dict
|
||||
stores the image UUID and the corresponding scg list , the other stores
|
||||
the scg UUID and the corresponding storage templates list.
|
||||
"""
|
||||
available_scg_list = utils.get_utils().get_our_scg_list()
|
||||
available_storage_template = \
|
||||
utils.get_utils().get_all_storage_templates()
|
||||
image_scgs_dict = \
|
||||
utils.get_utils().get_image_scgs_dict(available_scg_list)
|
||||
scg_storage_templates_dict = \
|
||||
utils.get_utils().\
|
||||
get_scg_accessible_storage_templates_extended(
|
||||
available_scg_list, available_storage_template)
|
||||
return image_scgs_dict, scg_storage_templates_dict
|
||||
|
||||
|
||||
class ImageSyncController():
|
||||
"""
|
||||
|
|
|
@ -14,6 +14,7 @@ PVM_HYPERVISOR_TYPE = "powervm"
|
|||
|
||||
# Flavor constants
|
||||
SCG_KEY = "powervm:storage_connectivity_group"
|
||||
STORAGE_TEMPLATE_KEY = "powervm:boot_volume_type"
|
||||
EXTRA_SPECS = "extra_specs"
|
||||
IS_PUBLIC = "os-flavor-access:is_public"
|
||||
|
||||
|
|
|
@ -653,11 +653,22 @@ class PowerVCService(object):
|
|||
"""
|
||||
createdServer = None
|
||||
|
||||
self.validate_update_scg(flavorDict)
|
||||
|
||||
# extract activation data from instance
|
||||
meta = instance._metadata
|
||||
key_name = instance.key_name
|
||||
|
||||
extra_specs_key = constants.EXTRA_SPECS
|
||||
scg_key = constants.SCG_KEY
|
||||
storage_template_key = constants.STORAGE_TEMPLATE_KEY
|
||||
|
||||
if 'selected-scg' in meta.keys() and \
|
||||
'selected-storage-template' in meta.keys():
|
||||
flavorDict[extra_specs_key][scg_key] = meta['selected-scg']
|
||||
flavorDict[extra_specs_key][storage_template_key] = \
|
||||
meta['selected-storage-template']
|
||||
|
||||
self.validate_update_scg(flavorDict)
|
||||
|
||||
# key_data = instance.key_data
|
||||
config_drive = instance._config_drive
|
||||
userdata = instance.user_data # already base64 encoded by local OS
|
||||
|
|
Loading…
Reference in New Issue