diff options
Diffstat (limited to 'nova/virt/powervm/driver.py')
-rw-r--r-- | nova/virt/powervm/driver.py | 708 |
1 files changed, 0 insertions, 708 deletions
diff --git a/nova/virt/powervm/driver.py b/nova/virt/powervm/driver.py deleted file mode 100644 index 0f94a3b75b..0000000000 --- a/nova/virt/powervm/driver.py +++ /dev/null @@ -1,708 +0,0 @@ -# Copyright 2014, 2018 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. -"""Connection to PowerVM hypervisor through NovaLink.""" - -import os_resource_classes as orc -from oslo_log import log as logging -from oslo_utils import excutils -from oslo_utils import importutils -from pypowervm import adapter as pvm_apt -from pypowervm import const as pvm_const -from pypowervm import exceptions as pvm_exc -from pypowervm.helpers import log_helper as log_hlp -from pypowervm.helpers import vios_busy as vio_hlp -from pypowervm.tasks import partition as pvm_par -from pypowervm.tasks import storage as pvm_stor -from pypowervm.tasks import vterm as pvm_vterm -from pypowervm.wrappers import managed_system as pvm_ms -from taskflow.patterns import linear_flow as tf_lf - -from nova.compute import task_states -from nova import conf as cfg -from nova.console import type as console_type -from nova import exception as exc -from nova.i18n import _ -from nova.image import glance -from nova.virt import configdrive -from nova.virt import driver -from nova.virt.powervm import host as pvm_host -from nova.virt.powervm.tasks import base as tf_base -from nova.virt.powervm.tasks import image as tf_img -from nova.virt.powervm.tasks import network as tf_net -from nova.virt.powervm.tasks import storage as tf_stg -from nova.virt.powervm.tasks import vm as tf_vm -from nova.virt.powervm import vm -from nova.virt.powervm import volume -from nova.virt.powervm.volume import fcvscsi - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - -DISK_ADPT_NS = 'nova.virt.powervm.disk' -DISK_ADPT_MAPPINGS = { - 'localdisk': 'localdisk.LocalStorage', - 'ssp': 'ssp.SSPDiskAdapter' -} - - -class PowerVMDriver(driver.ComputeDriver): - """PowerVM NovaLink Implementation of Compute Driver. - - https://wiki.openstack.org/wiki/PowerVM - """ - - def __init__(self, virtapi): - # NOTE(edmondsw) some of these will be dynamic in future, so putting - # capabilities on the instance rather than on the class. - self.capabilities = { - 'has_imagecache': False, - 'supports_bfv_rescue': False, - 'supports_evacuate': False, - 'supports_migrate_to_same_host': False, - 'supports_attach_interface': True, - 'supports_device_tagging': False, - 'supports_tagged_attach_interface': False, - 'supports_tagged_attach_volume': False, - 'supports_extend_volume': True, - 'supports_multiattach': False, - 'supports_trusted_certs': False, - 'supports_pcpus': False, - 'supports_accelerators': False, - 'supports_vtpm': False, - 'supports_secure_boot': False, - 'supports_socket_pci_numa_affinity': False, - - # Supported image types - "supports_image_type_aki": False, - "supports_image_type_ami": False, - "supports_image_type_ari": False, - "supports_image_type_iso": False, - "supports_image_type_qcow2": False, - "supports_image_type_raw": True, - "supports_image_type_vdi": False, - "supports_image_type_vhd": False, - "supports_image_type_vhdx": False, - "supports_image_type_vmdk": False, - "supports_image_type_ploop": False, - } - super(PowerVMDriver, self).__init__(virtapi) - - def init_host(self, host): - """Initialize anything that is necessary for the driver to function. - - Includes catching up with currently running VMs on the given host. - """ - LOG.warning( - 'The powervm virt driver is deprecated and may be removed in a ' - 'future release. The driver is not tested by the OpenStack ' - 'project nor does it have clear maintainers and thus its quality' - 'can not be ensured. If you are using the driver in production ' - 'please let us know the openstack-discuss mailing list or on IRC' - ) - - # Build the adapter. May need to attempt the connection multiple times - # in case the PowerVM management API service is starting. - # TODO(efried): Implement async compute service enable/disable like - # I73a34eb6e0ca32d03e54d12a5e066b2ed4f19a61 - self.adapter = pvm_apt.Adapter( - pvm_apt.Session(conn_tries=60), - helpers=[log_hlp.log_helper, vio_hlp.vios_busy_retry_helper]) - # Make sure the Virtual I/O Server(s) are available. - pvm_par.validate_vios_ready(self.adapter) - self.host_wrapper = pvm_ms.System.get(self.adapter)[0] - - # Do a scrub of the I/O plane to make sure the system is in good shape - LOG.info("Clearing stale I/O connections on driver init.") - pvm_stor.ComprehensiveScrub(self.adapter).execute() - - # Initialize the disk adapter - self.disk_dvr = importutils.import_object_ns( - DISK_ADPT_NS, DISK_ADPT_MAPPINGS[CONF.powervm.disk_driver.lower()], - self.adapter, self.host_wrapper.uuid) - self.image_api = glance.API() - - LOG.info("The PowerVM compute driver has been initialized.") - - @staticmethod - def _log_operation(op, instance): - """Log entry point of driver operations.""" - LOG.info('Operation: %(op)s. Virtual machine display name: ' - '%(display_name)s, name: %(name)s', - {'op': op, 'display_name': instance.display_name, - 'name': instance.name}, instance=instance) - - def get_info(self, instance, use_cache=True): - """Get the current status of an instance. - - :param instance: nova.objects.instance.Instance object - :param use_cache: unused in this driver - :returns: An InstanceInfo object. - """ - return vm.get_vm_info(self.adapter, instance) - - def list_instances(self): - """Return the names of all the instances known to the virt host. - - :return: VM Names as a list. - """ - return vm.get_lpar_names(self.adapter) - - def get_available_nodes(self, refresh=False): - """Returns nodenames of all nodes managed by the compute service. - - This method is for multi compute-nodes support. If a driver supports - multi compute-nodes, this method returns a list of nodenames managed - by the service. Otherwise, this method should return - [hypervisor_hostname]. - """ - - return [CONF.host] - - def get_available_resource(self, nodename): - """Retrieve resource information. - - This method is called when nova-compute launches, and as part of a - periodic task. - - :param nodename: Node from which the caller wants to get resources. - A driver that manages only one node can safely ignore - this. - :return: Dictionary describing resources. - """ - # Do this here so it refreshes each time this method is called. - self.host_wrapper = pvm_ms.System.get(self.adapter)[0] - return self._get_available_resource() - - def _get_available_resource(self): - # Get host information - data = pvm_host.build_host_resource_from_ms(self.host_wrapper) - - # Add the disk information - data["local_gb"] = self.disk_dvr.capacity - data["local_gb_used"] = self.disk_dvr.capacity_used - - return data - - def update_provider_tree(self, provider_tree, nodename, allocations=None): - """Update a ProviderTree with current provider and inventory data. - - :param nova.compute.provider_tree.ProviderTree provider_tree: - A nova.compute.provider_tree.ProviderTree object representing all - the providers in the tree associated with the compute node, and any - sharing providers (those with the ``MISC_SHARES_VIA_AGGREGATE`` - trait) associated via aggregate with any of those providers (but - not *their* tree- or aggregate-associated providers), as currently - known by placement. - :param nodename: - String name of the compute node (i.e. - ComputeNode.hypervisor_hostname) for which the caller is requesting - updated provider information. - :param allocations: Currently ignored by this driver. - """ - # Get (legacy) resource information. Same as get_available_resource, - # but we don't need to refresh self.host_wrapper as it was *just* - # refreshed by get_available_resource in the resource tracker's - # update_available_resource flow. - data = self._get_available_resource() - - # NOTE(yikun): If the inv record does not exists, the allocation_ratio - # will use the CONF.xxx_allocation_ratio value if xxx_allocation_ratio - # is set, and fallback to use the initial_xxx_allocation_ratio - # otherwise. - inv = provider_tree.data(nodename).inventory - ratios = self._get_allocation_ratios(inv) - # TODO(efried): Fix these to reflect something like reality - cpu_reserved = CONF.reserved_host_cpus - mem_reserved = CONF.reserved_host_memory_mb - disk_reserved = self._get_reserved_host_disk_gb_from_config() - - inventory = { - orc.VCPU: { - 'total': data['vcpus'], - 'max_unit': data['vcpus'], - 'allocation_ratio': ratios[orc.VCPU], - 'reserved': cpu_reserved, - }, - orc.MEMORY_MB: { - 'total': data['memory_mb'], - 'max_unit': data['memory_mb'], - 'allocation_ratio': ratios[orc.MEMORY_MB], - 'reserved': mem_reserved, - }, - orc.DISK_GB: { - # TODO(efried): Proper DISK_GB sharing when SSP driver in play - 'total': int(data['local_gb']), - 'max_unit': int(data['local_gb']), - 'allocation_ratio': ratios[orc.DISK_GB], - 'reserved': disk_reserved, - }, - } - provider_tree.update_inventory(nodename, inventory) - - def spawn(self, context, instance, image_meta, injected_files, - admin_password, allocations, network_info=None, - block_device_info=None, power_on=True, accel_info=None): - """Create a new instance/VM/domain on the virtualization platform. - - Once this successfully completes, the instance should be - running (power_state.RUNNING). - - If this fails, any partial instance should be completely - cleaned up, and the virtualization platform should be in the state - that it was before this call began. - - :param context: security context - :param instance: nova.objects.instance.Instance - This function should use the data there to guide - the creation of the new instance. - :param nova.objects.ImageMeta image_meta: - The metadata of the image of the instance. - :param injected_files: User files to inject into instance. - :param admin_password: Administrator password to set in instance. - :param allocations: Information about resources allocated to the - instance via placement, of the form returned by - SchedulerReportClient.get_allocations_for_consumer. - :param network_info: instance network information - :param block_device_info: Information about block devices to be - attached to the instance. - :param power_on: True if the instance should be powered on, False - otherwise - """ - self._log_operation('spawn', instance) - # Define the flow - flow_spawn = tf_lf.Flow("spawn") - - # This FeedTask accumulates VIOS storage connection operations to be - # run in parallel. Include both SCSI and fibre channel mappings for - # the scrubber. - stg_ftsk = pvm_par.build_active_vio_feed_task( - self.adapter, xag={pvm_const.XAG.VIO_SMAP, pvm_const.XAG.VIO_FMAP}) - - flow_spawn.add(tf_vm.Create( - self.adapter, self.host_wrapper, instance, stg_ftsk)) - - # Create a flow for the IO - flow_spawn.add(tf_net.PlugVifs( - self.virtapi, self.adapter, instance, network_info)) - flow_spawn.add(tf_net.PlugMgmtVif( - self.adapter, instance)) - - # Create the boot image. - flow_spawn.add(tf_stg.CreateDiskForImg( - self.disk_dvr, context, instance, image_meta)) - # Connects up the disk to the LPAR - flow_spawn.add(tf_stg.AttachDisk( - self.disk_dvr, instance, stg_ftsk=stg_ftsk)) - - # Extract the block devices. - bdms = driver.block_device_info_get_mapping(block_device_info) - - # Determine if there are volumes to connect. If so, add a connection - # for each type. - for bdm, vol_drv in self._vol_drv_iter(context, instance, bdms, - stg_ftsk=stg_ftsk): - # Connect the volume. This will update the connection_info. - flow_spawn.add(tf_stg.AttachVolume(vol_drv)) - - # If the config drive is needed, add those steps. Should be done - # after all the other I/O. - if configdrive.required_by(instance): - flow_spawn.add(tf_stg.CreateAndConnectCfgDrive( - self.adapter, instance, injected_files, network_info, - stg_ftsk, admin_pass=admin_password)) - - # Add the transaction manager flow at the end of the 'I/O - # connection' tasks. This will run all the connections in parallel. - flow_spawn.add(stg_ftsk) - - # Last step is to power on the system. - flow_spawn.add(tf_vm.PowerOn(self.adapter, instance)) - - # Run the flow. - tf_base.run(flow_spawn, instance=instance) - - def destroy(self, context, instance, network_info, block_device_info=None, - destroy_disks=True): - """Destroy the specified instance from the Hypervisor. - - If the instance is not found (for example if networking failed), this - function should still succeed. It's probably a good idea to log a - warning in that case. - - :param context: security context - :param instance: Instance object as returned by DB layer. - :param network_info: instance network information - :param block_device_info: Information about block devices that should - be detached from the instance. - :param destroy_disks: Indicates if disks should be destroyed - """ - # TODO(thorst, efried) Add resize checks for destroy - - self._log_operation('destroy', instance) - - def _setup_flow_and_run(): - # Define the flow - flow = tf_lf.Flow("destroy") - - # Power Off the LPAR. If its disks are about to be deleted, issue a - # hard shutdown. - flow.add(tf_vm.PowerOff(self.adapter, instance, - force_immediate=destroy_disks)) - - # The FeedTask accumulates storage disconnection tasks to be run in - # parallel. - stg_ftsk = pvm_par.build_active_vio_feed_task( - self.adapter, xag=[pvm_const.XAG.VIO_SMAP]) - - # Call the unplug VIFs task. While CNAs get removed from the LPAR - # directly on the destroy, this clears up the I/O Host side. - flow.add(tf_net.UnplugVifs(self.adapter, instance, network_info)) - - # Add the disconnect/deletion of the vOpt to the transaction - # manager. - if configdrive.required_by(instance): - flow.add(tf_stg.DeleteVOpt( - self.adapter, instance, stg_ftsk=stg_ftsk)) - - # Extract the block devices. - bdms = driver.block_device_info_get_mapping(block_device_info) - - # Determine if there are volumes to detach. If so, remove each - # volume (within the transaction manager) - for bdm, vol_drv in self._vol_drv_iter( - context, instance, bdms, stg_ftsk=stg_ftsk): - flow.add(tf_stg.DetachVolume(vol_drv)) - - # Detach the disk storage adapters - flow.add(tf_stg.DetachDisk(self.disk_dvr, instance)) - - # Accumulated storage disconnection tasks next - flow.add(stg_ftsk) - - # Delete the storage disks - if destroy_disks: - flow.add(tf_stg.DeleteDisk(self.disk_dvr)) - - # TODO(thorst, efried) Add LPAR id based scsi map clean up task - flow.add(tf_vm.Delete(self.adapter, instance)) - - # Build the engine & run! - tf_base.run(flow, instance=instance) - - try: - _setup_flow_and_run() - except exc.InstanceNotFound: - LOG.debug('VM was not found during destroy operation.', - instance=instance) - return - except pvm_exc.Error as e: - LOG.exception("PowerVM error during destroy.", instance=instance) - # Convert to a Nova exception - raise exc.InstanceTerminationFailure(reason=str(e)) - - 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. - :param update_task_state: Callback function to update the task_state - on the instance while the snapshot operation progresses. The - function takes a task_state argument and an optional - expected_task_state kwarg which defaults to - nova.compute.task_states.IMAGE_SNAPSHOT. See - nova.objects.instance.Instance.save for expected_task_state usage. - """ - - if not self.disk_dvr.capabilities.get('snapshot'): - raise exc.NotSupportedWithOption( - message=_("The snapshot operation is not supported in " - "conjunction with a [powervm]/disk_driver setting " - "of %s.") % CONF.powervm.disk_driver) - - self._log_operation('snapshot', instance) - - # Define the flow. - flow = tf_lf.Flow("snapshot") - - # Notify that we're starting the process. - flow.add(tf_img.UpdateTaskState(update_task_state, - task_states.IMAGE_PENDING_UPLOAD)) - - # Connect the instance's boot disk to the management partition, and - # scan the scsi bus and bring the device into the management partition. - flow.add(tf_stg.InstanceDiskToMgmt(self.disk_dvr, instance)) - - # Notify that the upload is in progress. - flow.add(tf_img.UpdateTaskState( - update_task_state, task_states.IMAGE_UPLOADING, - expected_state=task_states.IMAGE_PENDING_UPLOAD)) - - # Stream the disk to glance. - flow.add(tf_img.StreamToGlance(context, self.image_api, image_id, - instance)) - - # Disconnect the boot disk from the management partition and delete the - # device. - flow.add(tf_stg.RemoveInstanceDiskFromMgmt(self.disk_dvr, instance)) - - # Run the flow. - tf_base.run(flow, instance=instance) - - def power_off(self, instance, timeout=0, retry_interval=0): - """Power off the specified instance. - - :param instance: nova.objects.instance.Instance - :param timeout: time to wait for GuestOS to shutdown - :param retry_interval: How often to signal guest while - waiting for it to shutdown - """ - self._log_operation('power_off', instance) - force_immediate = (timeout == 0) - timeout = timeout or None - vm.power_off(self.adapter, instance, force_immediate=force_immediate, - timeout=timeout) - - def power_on(self, context, instance, network_info, - block_device_info=None, accel_info=None): - """Power on the specified instance. - - :param instance: nova.objects.instance.Instance - """ - self._log_operation('power_on', instance) - vm.power_on(self.adapter, instance) - - def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None, - accel_info=None): - """Reboot the specified instance. - - After this is called successfully, the instance's state - goes back to power_state.RUNNING. The virtualization - platform should ensure that the reboot action has completed - successfully even in cases in which the underlying domain/vm - is paused or halted/stopped. - - :param instance: nova.objects.instance.Instance - :param network_info: `nova.network.models.NetworkInfo` object - describing the network metadata. - :param reboot_type: Either a HARD or SOFT reboot - :param block_device_info: Info pertaining to attached volumes - :param bad_volumes_callback: Function to handle any bad volumes - encountered - :param accel_info: List of accelerator request dicts. The exact - data struct is doc'd in nova/virt/driver.py::spawn(). - """ - self._log_operation(reboot_type + ' reboot', instance) - vm.reboot(self.adapter, instance, reboot_type == 'HARD') - # pypowervm exceptions are sufficient to indicate real failure. - # Otherwise, pypowervm thinks the instance is up. - - def attach_interface(self, context, instance, image_meta, vif): - """Attach an interface to the instance.""" - self.plug_vifs(instance, [vif]) - - def detach_interface(self, context, instance, vif): - """Detach an interface from the instance.""" - self.unplug_vifs(instance, [vif]) - - def plug_vifs(self, instance, network_info): - """Plug VIFs into networks.""" - self._log_operation('plug_vifs', instance) - - # Define the flow - flow = tf_lf.Flow("plug_vifs") - - # Get the LPAR Wrapper - flow.add(tf_vm.Get(self.adapter, instance)) - - # Run the attach - flow.add(tf_net.PlugVifs(self.virtapi, self.adapter, instance, - network_info)) - - # Run the flow - try: - tf_base.run(flow, instance=instance) - except exc.InstanceNotFound: - raise exc.VirtualInterfacePlugException( - _("Plug vif failed because instance %s was not found.") - % instance.name) - except Exception: - LOG.exception("PowerVM error plugging vifs.", instance=instance) - raise exc.VirtualInterfacePlugException( - _("Plug vif failed because of an unexpected error.")) - - def unplug_vifs(self, instance, network_info): - """Unplug VIFs from networks.""" - self._log_operation('unplug_vifs', instance) - - # Define the flow - flow = tf_lf.Flow("unplug_vifs") - - # Run the detach - flow.add(tf_net.UnplugVifs(self.adapter, instance, network_info)) - - # Run the flow - try: - tf_base.run(flow, instance=instance) - except exc.InstanceNotFound: - LOG.warning('VM was not found during unplug operation as it is ' - 'already possibly deleted.', instance=instance) - except Exception: - LOG.exception("PowerVM error trying to unplug vifs.", - instance=instance) - raise exc.InterfaceDetachFailed(instance_uuid=instance.uuid) - - def get_vnc_console(self, context, instance): - """Get connection info for a vnc console. - - :param context: security context - :param instance: nova.objects.instance.Instance - - :return: An instance of console.type.ConsoleVNC - """ - self._log_operation('get_vnc_console', instance) - lpar_uuid = vm.get_pvm_uuid(instance) - - # Build the connection to the VNC. - host = CONF.vnc.server_proxyclient_address - # TODO(thorst, efried) Add the x509 certificate support when it lands - - try: - # Open up a remote vterm - port = pvm_vterm.open_remotable_vnc_vterm( - self.adapter, lpar_uuid, host, vnc_path=lpar_uuid) - # Note that the VNC viewer will wrap the internal_access_path with - # the HTTP content. - return console_type.ConsoleVNC(host=host, port=port, - internal_access_path=lpar_uuid) - except pvm_exc.HttpError as e: - with excutils.save_and_reraise_exception(logger=LOG) as sare: - # If the LPAR was not found, raise a more descriptive error - if e.response.status == 404: - sare.reraise = False - raise exc.InstanceNotFound(instance_id=instance.uuid) - - def attach_volume(self, context, connection_info, instance, mountpoint, - disk_bus=None, device_type=None, encryption=None): - """Attach the volume to the instance using the connection_info. - - :param context: security context - :param connection_info: Volume connection information from the block - device mapping - :param instance: nova.objects.instance.Instance - :param mountpoint: Unused - :param disk_bus: Unused - :param device_type: Unused - :param encryption: Unused - """ - self._log_operation('attach_volume', instance) - - # Define the flow - flow = tf_lf.Flow("attach_volume") - - # Build the driver - vol_drv = volume.build_volume_driver(self.adapter, instance, - connection_info) - - # Add the volume attach to the flow. - flow.add(tf_stg.AttachVolume(vol_drv)) - - # Run the flow - tf_base.run(flow, instance=instance) - - # The volume connector may have updated the system metadata. Save - # the instance to persist the data. Spawn/destroy auto saves instance, - # but the attach does not. Detach does not need this save - as the - # detach flows do not (currently) modify system metadata. May need - # to revise in the future as volume connectors evolve. - instance.save() - - def detach_volume(self, context, connection_info, instance, mountpoint, - encryption=None): - """Detach the volume attached to the instance. - - :param context: security context - :param connection_info: Volume connection information from the block - device mapping - :param instance: nova.objects.instance.Instance - :param mountpoint: Unused - :param encryption: Unused - """ - self._log_operation('detach_volume', instance) - - # Define the flow - flow = tf_lf.Flow("detach_volume") - - # Get a volume adapter for this volume - vol_drv = volume.build_volume_driver(self.adapter, instance, - connection_info) - - # Add a task to detach the volume - flow.add(tf_stg.DetachVolume(vol_drv)) - - # Run the flow - tf_base.run(flow, instance=instance) - - def extend_volume(self, context, connection_info, instance, - requested_size): - """Extend the disk attached to the instance. - - :param context: security context - :param dict connection_info: The connection for the extended volume. - :param nova.objects.instance.Instance instance: - The instance whose volume gets extended. - :param int requested_size: The requested new volume size in bytes. - :return: None - """ - - vol_drv = volume.build_volume_driver( - self.adapter, instance, connection_info) - vol_drv.extend_volume() - - def _vol_drv_iter(self, context, instance, bdms, stg_ftsk=None): - """Yields a bdm and volume driver. - - :param context: security context - :param instance: nova.objects.instance.Instance - :param bdms: block device mappings - :param stg_ftsk: storage FeedTask - """ - # Get a volume driver for each volume - for bdm in bdms or []: - conn_info = bdm.get('connection_info') - vol_drv = volume.build_volume_driver(self.adapter, instance, - conn_info, stg_ftsk=stg_ftsk) - yield bdm, vol_drv - - def get_volume_connector(self, instance): - """Get connector information for the instance for attaching to volumes. - - Connector information is a dictionary representing information about - the system that will be making the connection. - - :param instance: nova.objects.instance.Instance - """ - # Put the values in the connector - connector = {} - wwpn_list = fcvscsi.wwpns(self.adapter) - - if wwpn_list is not None: - connector["wwpns"] = wwpn_list - connector["multipath"] = False - connector['host'] = CONF.host - connector['initiator'] = None - - return connector |