summaryrefslogtreecommitdiff
path: root/nova/virt/powervm/vm.py
diff options
context:
space:
mode:
Diffstat (limited to 'nova/virt/powervm/vm.py')
-rw-r--r--nova/virt/powervm/vm.py543
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