182 lines
7.2 KiB
Python
182 lines
7.2 KiB
Python
# Copyright 2017 IBM Corp.
|
|
#
|
|
# All Rights Reserved.
|
|
#
|
|
# 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 abc
|
|
import six
|
|
from taskflow import task
|
|
|
|
from nova_powervm import conf as cfg
|
|
from nova_powervm.virt.powervm import exception as p_exc
|
|
from nova_powervm.virt.powervm import vm
|
|
from nova_powervm.virt.powervm.volume import driver as v_driver
|
|
from oslo_log import log as logging
|
|
from pypowervm import const as pvm_const
|
|
from pypowervm.tasks import client_storage as pvm_c_stor
|
|
from pypowervm.tasks import hdisk
|
|
from pypowervm.tasks import partition
|
|
from pypowervm.tasks import scsi_mapper as tsk_map
|
|
from pypowervm.wrappers import storage as pvm_stg
|
|
|
|
CONF = cfg.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class RBDVolumeAdapter(v_driver.PowerVMVolumeAdapter):
|
|
"""Base class for connecting ceph based Cinder Volumes to PowerVM VMs."""
|
|
|
|
def __init__(self, adapter, host_uuid, instance, connection_info,
|
|
stg_ftsk=None):
|
|
super(RBDVolumeAdapter, self).__init__(
|
|
adapter, host_uuid, instance, connection_info, stg_ftsk=stg_ftsk)
|
|
self._nl_vios_ids = None
|
|
|
|
@classmethod
|
|
def min_xags(cls):
|
|
return [pvm_const.XAG.VIO_SMAP]
|
|
|
|
@classmethod
|
|
def vol_type(cls):
|
|
"""The type of volume supported by this type."""
|
|
return 'rbd'
|
|
|
|
@property
|
|
def vios_uuids(self):
|
|
"""List the UUIDs of the Virtual I/O Servers hosting the storage."""
|
|
# Get the hosting UUID
|
|
if self._nl_vios_ids is None:
|
|
nl_vios_wrap = partition.get_mgmt_partition(self.adapter)
|
|
self._nl_vios_ids = [nl_vios_wrap.uuid]
|
|
return self._nl_vios_ids
|
|
|
|
def pre_live_migration_on_destination(self, mig_data):
|
|
"""Perform pre live migration steps for the volume on the target host.
|
|
|
|
This method performs any pre live migration that is needed.
|
|
|
|
This method will be called after the pre_live_migration_on_source
|
|
method. The data from the pre_live call will be passed in via the
|
|
mig_data. This method should put its output into the dest_mig_data.
|
|
|
|
:param mig_data: Dict of migration data for the destination server.
|
|
If the volume connector needs to provide
|
|
information to the live_migration command, it
|
|
should be added to this dictionary.
|
|
"""
|
|
for vios_uuid in self.vios_uuids:
|
|
exists, name = self.is_volume_on_vios(vios_uuid)
|
|
if exists and name is not None:
|
|
return
|
|
name = self.connection_info["data"]["name"]
|
|
LOG.warning("RBD %s not found", name, instance=self.instance)
|
|
raise p_exc.VolumePreMigrationFailed(
|
|
volume_id=self.volume_id, instance_name=self.instance.name)
|
|
|
|
def _connect_volume(self, slot_mgr):
|
|
name = self.connection_info["data"]["name"]
|
|
volid = self.connection_info["data"]["volume_id"]
|
|
user = CONF.powervm.rbd_user
|
|
rbd = pvm_stg.RBD.bld_ref(self.adapter, name, tag=volid, user=user)
|
|
|
|
def add_func(vios_w):
|
|
# If the vios doesn't match, just return
|
|
if vios_w.uuid not in self.vios_uuids:
|
|
return None
|
|
|
|
LOG.info("Adding rbd disk connection to VIOS %(vios)s.",
|
|
{'vios': vios_w.name}, instance=self.instance)
|
|
slot, lua = slot_mgr.build_map.get_vscsi_slot(vios_w, name)
|
|
if slot_mgr.is_rebuild and not slot:
|
|
LOG.debug('Detected a device with path %(path)s on VIOS '
|
|
'%(vios)s on the rebuild that did not exist on the '
|
|
'source. Ignoring.',
|
|
{'path': name, 'vios': vios_w.uuid},
|
|
instance=self.instance)
|
|
return None
|
|
|
|
mapping = tsk_map.build_vscsi_mapping(
|
|
self.host_uuid, vios_w, self.vm_uuid, rbd, lpar_slot_num=slot,
|
|
lua=lua)
|
|
return tsk_map.add_map(vios_w, mapping)
|
|
|
|
self.stg_ftsk.add_functor_subtask(add_func)
|
|
|
|
# Run after all the deferred tasks the query to save the slots in the
|
|
# slot map.
|
|
def set_slot_info():
|
|
vios_wraps = self.stg_ftsk.feed
|
|
partition_id = vm.get_vm_id(self.adapter, self.vm_uuid)
|
|
for vios_w in vios_wraps:
|
|
scsi_map = pvm_c_stor.udid_to_scsi_mapping(
|
|
vios_w, name, partition_id)
|
|
if not scsi_map:
|
|
continue
|
|
slot_mgr.register_vscsi_mapping(scsi_map)
|
|
|
|
self.stg_ftsk.add_post_execute(task.FunctorTask(
|
|
set_slot_info, name='rbd_slot_%s' % name))
|
|
|
|
def _disconnect_volume(self, slot_mgr):
|
|
# Build the match function
|
|
name = self.connection_info["data"]["name"]
|
|
match_func = tsk_map.gen_match_func(pvm_stg.VDisk,
|
|
names=[name])
|
|
|
|
# Make sure the remove function will run within the transaction manager
|
|
def rm_func(vios_w):
|
|
# If the vios doesn't match, just return
|
|
if vios_w.uuid not in self.vios_uuids:
|
|
return None
|
|
|
|
LOG.info("Disconnecting instance %(inst)s from storage "
|
|
"disks.", {'inst': self.instance.name},
|
|
instance=self.instance)
|
|
removed_maps = tsk_map.remove_maps(vios_w, self.vm_uuid,
|
|
match_func=match_func)
|
|
for rm_map in removed_maps:
|
|
slot_mgr.drop_vscsi_mapping(rm_map)
|
|
return removed_maps
|
|
|
|
self.stg_ftsk.add_functor_subtask(rm_func)
|
|
# Find the disk directly.
|
|
vios_w = self.stg_ftsk.wrapper_tasks[self.vios_uuids[0]].wrapper
|
|
mappings = tsk_map.find_maps(vios_w.scsi_mappings,
|
|
client_lpar_id=self.vm_uuid,
|
|
match_func=match_func)
|
|
|
|
return [x.backing_storage for x in mappings]
|
|
|
|
def is_volume_on_vios(self, vio):
|
|
"""Returns whether or not the volume file is on a VIOS.
|
|
|
|
This method is used during live-migration and rebuild to
|
|
check if the volume is available on the target host.
|
|
|
|
:param vio: The Virtual I/O Server wrapper or UDID string of the VIOS.
|
|
:return: True if the file is on the VIOS. False
|
|
otherwise.
|
|
:return: The file path.
|
|
"""
|
|
try:
|
|
vio_uuid = vio.uuid
|
|
except AttributeError:
|
|
vio_uuid = vio
|
|
if vio_uuid not in self.vios_uuids:
|
|
return False, None
|
|
name = self.connection_info["data"]["name"]
|
|
exists = hdisk.rbd_exists(self.adapter, vio_uuid, name)
|
|
return exists, name if exists else None
|