diff options
Diffstat (limited to 'nova/virt/powervm/vm.py')
-rw-r--r-- | nova/virt/powervm/vm.py | 543 |
1 files changed, 0 insertions, 543 deletions
diff --git a/nova/virt/powervm/vm.py b/nova/virt/powervm/vm.py deleted file mode 100644 index 2e5247551f..0000000000 --- a/nova/virt/powervm/vm.py +++ /dev/null @@ -1,543 +0,0 @@ -# Copyright 2014, 2017 IBM Corp. -# -# 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 re - -from oslo_concurrency import lockutils -from oslo_log import log as logging -from oslo_serialization import jsonutils -from oslo_utils import excutils -from oslo_utils import strutils as stru -from pypowervm import exceptions as pvm_exc -from pypowervm.helpers import log_helper as pvm_log -from pypowervm.tasks import power -from pypowervm.tasks import power_opts as popts -from pypowervm.tasks import vterm -from pypowervm import util as pvm_u -from pypowervm.utils import lpar_builder as lpar_bldr -from pypowervm.utils import uuid as pvm_uuid -from pypowervm.utils import validation as pvm_vldn -from pypowervm.wrappers import base_partition as pvm_bp -from pypowervm.wrappers import logical_partition as pvm_lpar -from pypowervm.wrappers import network as pvm_net -from pypowervm.wrappers import shared_proc_pool as pvm_spp - -from nova.compute import power_state -from nova import conf -from nova import exception as exc -from nova.i18n import _ -from nova.virt import hardware - - -CONF = conf.CONF -LOG = logging.getLogger(__name__) - -_POWERVM_STARTABLE_STATE = (pvm_bp.LPARState.NOT_ACTIVATED,) -_POWERVM_STOPPABLE_STATE = ( - pvm_bp.LPARState.RUNNING, pvm_bp.LPARState.STARTING, - pvm_bp.LPARState.OPEN_FIRMWARE, pvm_bp.LPARState.SHUTTING_DOWN, - pvm_bp.LPARState.ERROR, pvm_bp.LPARState.RESUMING, - pvm_bp.LPARState.SUSPENDING) -_POWERVM_TO_NOVA_STATE = { - pvm_bp.LPARState.MIGRATING_RUNNING: power_state.RUNNING, - pvm_bp.LPARState.RUNNING: power_state.RUNNING, - pvm_bp.LPARState.STARTING: power_state.RUNNING, - # map open firmware state to active since it can be shut down - pvm_bp.LPARState.OPEN_FIRMWARE: power_state.RUNNING, - # It is running until it is off. - pvm_bp.LPARState.SHUTTING_DOWN: power_state.RUNNING, - # It is running until the suspend completes - pvm_bp.LPARState.SUSPENDING: power_state.RUNNING, - - pvm_bp.LPARState.MIGRATING_NOT_ACTIVE: power_state.SHUTDOWN, - pvm_bp.LPARState.NOT_ACTIVATED: power_state.SHUTDOWN, - - pvm_bp.LPARState.UNKNOWN: power_state.NOSTATE, - pvm_bp.LPARState.HARDWARE_DISCOVERY: power_state.NOSTATE, - pvm_bp.LPARState.NOT_AVAILBLE: power_state.NOSTATE, - - # While resuming, we should be considered suspended still. Only once - # resumed will we be active (which is represented by the RUNNING state) - pvm_bp.LPARState.RESUMING: power_state.SUSPENDED, - pvm_bp.LPARState.SUSPENDED: power_state.SUSPENDED, - - pvm_bp.LPARState.ERROR: power_state.CRASHED} - - -def get_cnas(adapter, instance, **search): - """Returns the (possibly filtered) current CNAs on the instance. - - The Client Network Adapters are the Ethernet adapters for a VM. - - :param adapter: The pypowervm adapter. - :param instance: The nova instance. - :param search: Keyword arguments for CNA.search. If omitted, all CNAs are - returned. - :return The CNA wrappers that represent the ClientNetworkAdapters on the VM - """ - meth = pvm_net.CNA.search if search else pvm_net.CNA.get - - return meth(adapter, parent_type=pvm_lpar.LPAR, - parent_uuid=get_pvm_uuid(instance), **search) - - -def get_lpar_names(adp): - """Get a list of the LPAR names. - - :param adp: A pypowervm.adapter.Adapter instance for the PowerVM API. - :return: A list of string names of the PowerVM Logical Partitions. - """ - return [x.name for x in pvm_lpar.LPAR.search(adp, is_mgmt_partition=False)] - - -def get_pvm_uuid(instance): - """Get the corresponding PowerVM VM uuid of an instance uuid. - - Maps a OpenStack instance uuid to a PowerVM uuid. The UUID between the - Nova instance and PowerVM will be 1 to 1 mapped. This method runs the - algorithm against the instance's uuid to convert it to the PowerVM - UUID. - - :param instance: nova.objects.instance.Instance. - :return: The PowerVM UUID for the LPAR corresponding to the instance. - """ - return pvm_uuid.convert_uuid_to_pvm(instance.uuid).upper() - - -def get_instance_wrapper(adapter, instance): - """Get the LPAR wrapper for a given Nova instance. - - :param adapter: The adapter for the pypowervm API - :param instance: The nova instance. - :return: The pypowervm logical_partition wrapper. - """ - pvm_inst_uuid = get_pvm_uuid(instance) - try: - return pvm_lpar.LPAR.get(adapter, uuid=pvm_inst_uuid) - except pvm_exc.Error as e: - with excutils.save_and_reraise_exception(logger=LOG) as sare: - LOG.exception("Failed to retrieve LPAR associated with instance.", - instance=instance) - if e.response is not None and e.response.status == 404: - sare.reraise = False - raise exc.InstanceNotFound(instance_id=pvm_inst_uuid) - - -def power_on(adapter, instance): - """Powers on a VM. - - :param adapter: A pypowervm.adapter.Adapter. - :param instance: The nova instance to power on. - :raises: InstancePowerOnFailure - """ - # Synchronize power-on and power-off ops on a given instance - with lockutils.lock('power_%s' % instance.uuid): - entry = get_instance_wrapper(adapter, instance) - # Get the current state and see if we can start the VM - if entry.state in _POWERVM_STARTABLE_STATE: - # Now start the lpar - try: - power.power_on(entry, None) - except pvm_exc.Error as e: - LOG.exception("PowerVM error during power_on.", - instance=instance) - raise exc.InstancePowerOnFailure(reason=str(e)) - - -def power_off(adapter, instance, force_immediate=False, timeout=None): - """Powers off a VM. - - :param adapter: A pypowervm.adapter.Adapter. - :param instance: The nova instance to power off. - :param timeout: (Optional, Default None) How long to wait for the job - to complete. By default, is None which indicates it should - use the default from pypowervm's power off method. - :param force_immediate: (Optional, Default False) Should it be immediately - shut down. - :raises: InstancePowerOffFailure - """ - # Synchronize power-on and power-off ops on a given instance - with lockutils.lock('power_%s' % instance.uuid): - entry = get_instance_wrapper(adapter, instance) - # Get the current state and see if we can stop the VM - LOG.debug("Powering off request for instance in state %(state)s. " - "Force Immediate Flag: %(force)s.", - {'state': entry.state, 'force': force_immediate}, - instance=instance) - if entry.state in _POWERVM_STOPPABLE_STATE: - # Now stop the lpar - try: - LOG.debug("Power off executing.", instance=instance) - kwargs = {'timeout': timeout} if timeout else {} - if force_immediate: - power.PowerOp.stop( - entry, opts=popts.PowerOffOpts().vsp_hard(), **kwargs) - else: - power.power_off_progressive(entry, **kwargs) - except pvm_exc.Error as e: - LOG.exception("PowerVM error during power_off.", - instance=instance) - raise exc.InstancePowerOffFailure(reason=str(e)) - else: - LOG.debug("Power off not required for instance %(inst)s.", - {'inst': instance.name}) - - -def reboot(adapter, instance, hard): - """Reboots a VM. - - :param adapter: A pypowervm.adapter.Adapter. - :param instance: The nova instance to reboot. - :param hard: Boolean True if hard reboot, False otherwise. - :raises: InstanceRebootFailure - """ - # Synchronize power-on and power-off ops on a given instance - with lockutils.lock('power_%s' % instance.uuid): - try: - entry = get_instance_wrapper(adapter, instance) - if entry.state != pvm_bp.LPARState.NOT_ACTIVATED: - if hard: - power.PowerOp.stop( - entry, opts=popts.PowerOffOpts().vsp_hard().restart()) - else: - power.power_off_progressive(entry, restart=True) - else: - # pypowervm does NOT throw an exception if "already down". - # Any other exception from pypowervm is a legitimate failure; - # let it raise up. - # If we get here, pypowervm thinks the instance is down. - power.power_on(entry, None) - except pvm_exc.Error as e: - LOG.exception("PowerVM error during reboot.", instance=instance) - raise exc.InstanceRebootFailure(reason=str(e)) - - -def delete_lpar(adapter, instance): - """Delete an LPAR. - - :param adapter: The adapter for the pypowervm API. - :param instance: The nova instance corresponding to the lpar to delete. - """ - lpar_uuid = get_pvm_uuid(instance) - # Attempt to delete the VM. To avoid failures due to open vterm, we will - # attempt to close the vterm before issuing the delete. - try: - LOG.info('Deleting virtual machine.', instance=instance) - # Ensure any vterms are closed. Will no-op otherwise. - vterm.close_vterm(adapter, lpar_uuid) - # Run the LPAR delete - resp = adapter.delete(pvm_lpar.LPAR.schema_type, root_id=lpar_uuid) - LOG.info('Virtual machine delete status: %d', resp.status, - instance=instance) - return resp - except pvm_exc.HttpError as e: - with excutils.save_and_reraise_exception(logger=LOG) as sare: - if e.response and e.response.status == 404: - # LPAR is already gone - don't fail - sare.reraise = False - LOG.info('Virtual Machine not found', instance=instance) - else: - LOG.error('HttpError deleting virtual machine.', - instance=instance) - except pvm_exc.Error: - with excutils.save_and_reraise_exception(logger=LOG): - # Attempting to close vterm did not help so raise exception - LOG.error('Virtual machine delete failed: LPARID=%s', lpar_uuid) - - -def create_lpar(adapter, host_w, instance): - """Create an LPAR based on the host based on the instance. - - :param adapter: The adapter for the pypowervm API. - :param host_w: The host's System wrapper. - :param instance: The nova instance. - :return: The LPAR wrapper response from the API. - """ - try: - # Translate the nova flavor into a PowerVM Wrapper Object. - lpar_b = VMBuilder(host_w, adapter).lpar_builder(instance) - pending_lpar_w = lpar_b.build() - # Run validation against it. This is just for nice(r) error messages. - pvm_vldn.LPARWrapperValidator(pending_lpar_w, - host_w).validate_all() - # Create it. The API returns a new wrapper with the actual system data. - return pending_lpar_w.create(parent=host_w) - except lpar_bldr.LPARBuilderException as e: - # Raise the BuildAbortException since LPAR failed to build - raise exc.BuildAbortException(instance_uuid=instance.uuid, reason=e) - except pvm_exc.HttpError as he: - # Raise the API exception - LOG.exception("PowerVM HttpError creating LPAR.", instance=instance) - raise exc.PowerVMAPIFailed(inst_name=instance.name, reason=he) - - -def _translate_vm_state(pvm_state): - """Find the current state of the lpar. - - :return: The appropriate integer state value from power_state, converted - from the PowerVM state. - """ - if pvm_state is None: - return power_state.NOSTATE - try: - return _POWERVM_TO_NOVA_STATE[pvm_state.lower()] - except KeyError: - return power_state.NOSTATE - - -def get_vm_qp(adapter, lpar_uuid, qprop=None, log_errors=True): - """Returns one or all quick properties of an LPAR. - - :param adapter: The pypowervm adapter. - :param lpar_uuid: The (powervm) UUID for the LPAR. - :param qprop: The quick property key to return. If specified, that single - property value is returned. If None/unspecified, all quick - properties are returned in a dictionary. - :param log_errors: Indicator whether to log REST data after an exception - :return: Either a single quick property value or a dictionary of all quick - properties. - """ - kwds = dict(root_id=lpar_uuid, suffix_type='quick', suffix_parm=qprop) - if not log_errors: - # Remove the log helper from the list of helpers. - # Note that adapter.helpers returns a copy - the .remove doesn't affect - # the adapter's original helpers list. - helpers = adapter.helpers - try: - helpers.remove(pvm_log.log_helper) - except ValueError: - # It's not an error if we didn't find it. - pass - kwds['helpers'] = helpers - try: - resp = adapter.read(pvm_lpar.LPAR.schema_type, **kwds) - except pvm_exc.HttpError as e: - with excutils.save_and_reraise_exception(logger=LOG) as sare: - # 404 error indicates the LPAR has been deleted - if e.response and e.response.status == 404: - sare.reraise = False - raise exc.InstanceNotFound(instance_id=lpar_uuid) - # else raise the original exception - return jsonutils.loads(resp.body) - - -def get_vm_info(adapter, instance): - """Get the InstanceInfo for an instance. - - :param adapter: The pypowervm.adapter.Adapter for the PowerVM REST API. - :param instance: nova.objects.instance.Instance object - :returns: An InstanceInfo object. - """ - pvm_uuid = get_pvm_uuid(instance) - pvm_state = get_vm_qp(adapter, pvm_uuid, 'PartitionState') - nova_state = _translate_vm_state(pvm_state) - return hardware.InstanceInfo(nova_state) - - -def norm_mac(mac): - """Normalizes a MAC address from pypowervm format to OpenStack. - - That means that the format will be converted to lower case and will - have colons added. - - :param mac: A pypowervm mac address. Ex. 1234567890AB - :return: A mac that matches the standard neutron format. - Ex. 12:34:56:78:90:ab - """ - # Need the replacement if the mac is already normalized. - mac = mac.lower().replace(':', '') - return ':'.join(mac[i:i + 2] for i in range(0, len(mac), 2)) - - -class VMBuilder(object): - """Converts a Nova Instance/Flavor into a pypowervm LPARBuilder.""" - _PVM_PROC_COMPAT = 'powervm:processor_compatibility' - _PVM_UNCAPPED = 'powervm:uncapped' - _PVM_DED_SHAR_MODE = 'powervm:dedicated_sharing_mode' - _PVM_SHAR_PROC_POOL = 'powervm:shared_proc_pool_name' - _PVM_SRR_CAPABILITY = 'powervm:srr_capability' - - # Map of PowerVM extra specs to the lpar builder attributes. - # '' is used for attributes that are not implemented yet. - # None means there is no direct attribute mapping and must - # be handled individually - _ATTRS_MAP = { - 'powervm:min_mem': lpar_bldr.MIN_MEM, - 'powervm:max_mem': lpar_bldr.MAX_MEM, - 'powervm:min_vcpu': lpar_bldr.MIN_VCPU, - 'powervm:max_vcpu': lpar_bldr.MAX_VCPU, - 'powervm:proc_units': lpar_bldr.PROC_UNITS, - 'powervm:min_proc_units': lpar_bldr.MIN_PROC_U, - 'powervm:max_proc_units': lpar_bldr.MAX_PROC_U, - 'powervm:dedicated_proc': lpar_bldr.DED_PROCS, - 'powervm:shared_weight': lpar_bldr.UNCAPPED_WEIGHT, - 'powervm:availability_priority': lpar_bldr.AVAIL_PRIORITY, - _PVM_UNCAPPED: None, - _PVM_DED_SHAR_MODE: None, - _PVM_PROC_COMPAT: None, - _PVM_SHAR_PROC_POOL: None, - _PVM_SRR_CAPABILITY: None, - } - - _DED_SHARING_MODES_MAP = { - 'share_idle_procs': pvm_bp.DedicatedSharingMode.SHARE_IDLE_PROCS, - 'keep_idle_procs': pvm_bp.DedicatedSharingMode.KEEP_IDLE_PROCS, - 'share_idle_procs_active': - pvm_bp.DedicatedSharingMode.SHARE_IDLE_PROCS_ACTIVE, - 'share_idle_procs_always': - pvm_bp.DedicatedSharingMode.SHARE_IDLE_PROCS_ALWAYS, - } - - def __init__(self, host_w, adapter): - """Initialize the converter. - - :param host_w: The host System wrapper. - :param adapter: The pypowervm.adapter.Adapter for the PowerVM REST API. - """ - self.adapter = adapter - self.host_w = host_w - kwargs = dict(proc_units_factor=CONF.powervm.proc_units_factor) - self.stdz = lpar_bldr.DefaultStandardize(host_w, **kwargs) - - def lpar_builder(self, inst): - """Returns the pypowervm LPARBuilder for a given Nova flavor. - - :param inst: the VM instance - """ - attrs = self._format_flavor(inst) - # TODO(thorst, efried) Add in IBMi attributes - return lpar_bldr.LPARBuilder(self.adapter, attrs, self.stdz) - - def _format_flavor(self, inst): - """Returns the pypowervm format of the flavor. - - :param inst: The Nova VM instance. - :return: A dict that can be used by the LPAR builder. - """ - # The attrs are what is sent to pypowervm to convert the lpar. - attrs = { - lpar_bldr.NAME: pvm_u.sanitize_partition_name_for_api(inst.name), - # The uuid is only actually set on a create of an LPAR - lpar_bldr.UUID: get_pvm_uuid(inst), - lpar_bldr.MEM: inst.flavor.memory_mb, - lpar_bldr.VCPU: inst.flavor.vcpus, - # Set the srr capability to True by default - lpar_bldr.SRR_CAPABLE: True} - - # Loop through the extra specs and process powervm keys - for key in inst.flavor.extra_specs.keys(): - # If it is not a valid key, then can skip. - if not self._is_pvm_valid_key(key): - continue - - # Look for the mapping to the lpar builder - bldr_key = self._ATTRS_MAP.get(key) - - # Check for no direct mapping, if the value is none, need to - # derive the complex type - if bldr_key is None: - self._build_complex_type(key, attrs, inst.flavor) - else: - # We found a direct mapping - attrs[bldr_key] = inst.flavor.extra_specs[key] - - return attrs - - def _is_pvm_valid_key(self, key): - """Will return if this is a valid PowerVM key. - - :param key: The powervm key. - :return: True if valid key. False if non-powervm key and should be - skipped. - """ - # If not a powervm key, then it is not 'pvm_valid' - if not key.startswith('powervm:'): - return False - - # Check if this is a valid attribute - if key not in self._ATTRS_MAP: - # Could be a key from a future release - warn, but ignore. - LOG.warning("Unhandled PowerVM key '%s'.", key) - return False - - return True - - def _build_complex_type(self, key, attrs, flavor): - """If a key does not directly map, this method derives the right value. - - Some types are complex, in that the flavor may have one key that maps - to several different attributes in the lpar builder. This method - handles the complex types. - - :param key: The flavor's key. - :param attrs: The attribute map to put the value into. - :param flavor: The Nova instance flavor. - :return: The value to put in for the key. - """ - # Map uncapped to sharing mode - if key == self._PVM_UNCAPPED: - attrs[lpar_bldr.SHARING_MODE] = ( - pvm_bp.SharingMode.UNCAPPED - if stru.bool_from_string(flavor.extra_specs[key], strict=True) - else pvm_bp.SharingMode.CAPPED) - elif key == self._PVM_DED_SHAR_MODE: - # Dedicated sharing modes...map directly - shr_mode_key = flavor.extra_specs[key] - mode = self._DED_SHARING_MODES_MAP.get(shr_mode_key) - if mode is None: - raise exc.InvalidParameterValue(err=_( - "Invalid dedicated sharing mode '%s'!") % shr_mode_key) - attrs[lpar_bldr.SHARING_MODE] = mode - elif key == self._PVM_SHAR_PROC_POOL: - pool_name = flavor.extra_specs[key] - attrs[lpar_bldr.SPP] = self._spp_pool_id(pool_name) - elif key == self._PVM_PROC_COMPAT: - # Handle variants of the supported values - attrs[lpar_bldr.PROC_COMPAT] = re.sub( - r'\+', '_Plus', flavor.extra_specs[key]) - elif key == self._PVM_SRR_CAPABILITY: - attrs[lpar_bldr.SRR_CAPABLE] = stru.bool_from_string( - flavor.extra_specs[key], strict=True) - else: - # There was no mapping or we didn't handle it. This is a BUG! - raise KeyError(_( - "Unhandled PowerVM key '%s'! Please report this bug.") % key) - - def _spp_pool_id(self, pool_name): - """Returns the shared proc pool id for a given pool name. - - :param pool_name: The shared proc pool name. - :return: The internal API id for the shared proc pool. - """ - if (pool_name is None or - pool_name == pvm_spp.DEFAULT_POOL_DISPLAY_NAME): - # The default pool is 0 - return 0 - - # Search for the pool with this name - pool_wraps = pvm_spp.SharedProcPool.search( - self.adapter, name=pool_name, parent=self.host_w) - - # Check to make sure there is a pool with the name, and only one pool. - if len(pool_wraps) > 1: - msg = (_('Multiple Shared Processing Pools with name %(pool)s.') % - {'pool': pool_name}) - raise exc.ValidationError(msg) - elif len(pool_wraps) == 0: - msg = (_('Unable to find Shared Processing Pool %(pool)s') % - {'pool': pool_name}) - raise exc.ValidationError(msg) - - # Return the singular pool id. - return pool_wraps[0].id |