Merge "PowerVM Driver: DiskAdapter parent class"
This commit is contained in:
commit
050e9202f6
|
@ -0,0 +1,51 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
from nova.virt.powervm.disk import driver as disk_dvr
|
||||
|
||||
|
||||
class FakeDiskAdapter(disk_dvr.DiskAdapter):
|
||||
"""A fake subclass of DiskAdapter.
|
||||
|
||||
This is done so that the abstract methods/properties can be stubbed and the
|
||||
class can be instantiated for testing.
|
||||
"""
|
||||
def _vios_uuids(self):
|
||||
pass
|
||||
|
||||
def _disk_match_func(self, disk_type, instance):
|
||||
pass
|
||||
|
||||
def disconnect_disk_from_mgmt(self, vios_uuid, disk_name):
|
||||
pass
|
||||
|
||||
def capacity(self):
|
||||
pass
|
||||
|
||||
def capacity_used(self):
|
||||
pass
|
||||
|
||||
def detach_disk(self, instance):
|
||||
pass
|
||||
|
||||
def delete_disks(self, storage_elems):
|
||||
pass
|
||||
|
||||
def create_disk_from_image(self, context, instance, image_meta):
|
||||
pass
|
||||
|
||||
def attach_disk(self, instance, disk_info, stg_ftsk):
|
||||
pass
|
|
@ -0,0 +1,59 @@
|
|||
# Copyright 2015, 2018 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 fixtures
|
||||
import mock
|
||||
from pypowervm import const as pvm_const
|
||||
|
||||
from nova import test
|
||||
from nova.tests.unit.virt.powervm.disk import fake_adapter
|
||||
|
||||
|
||||
class TestDiskAdapter(test.NoDBTestCase):
|
||||
"""Unit Tests for the generic storage driver."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestDiskAdapter, self).setUp()
|
||||
|
||||
# Return the mgmt uuid
|
||||
self.mgmt_uuid = self.useFixture(fixtures.MockPatch(
|
||||
'nova.virt.powervm.mgmt.mgmt_uuid')).mock
|
||||
self.mgmt_uuid.return_value = 'mp_uuid'
|
||||
|
||||
# The values (adapter and host uuid) are not used in the base.
|
||||
# Default them to None. We use the fake adapter here because we can't
|
||||
# instantiate DiskAdapter which is an abstract base class.
|
||||
self.st_adpt = fake_adapter.FakeDiskAdapter(None, None)
|
||||
|
||||
@mock.patch("pypowervm.util.sanitize_file_name_for_api")
|
||||
def test_get_disk_name(self, mock_san):
|
||||
inst = mock.Mock()
|
||||
inst.configure_mock(name='a_name_that_is_longer_than_eight',
|
||||
uuid='01234567-abcd-abcd-abcd-123412341234')
|
||||
|
||||
# Long
|
||||
self.assertEqual(mock_san.return_value,
|
||||
self.st_adpt._get_disk_name('type', inst))
|
||||
mock_san.assert_called_with(inst.name, prefix='type_',
|
||||
max_len=pvm_const.MaxLen.FILENAME_DEFAULT)
|
||||
|
||||
mock_san.reset_mock()
|
||||
|
||||
# Short
|
||||
self.assertEqual(mock_san.return_value,
|
||||
self.st_adpt._get_disk_name('type', inst, short=True))
|
||||
mock_san.assert_called_with('a_name_t_0123', prefix='t_',
|
||||
max_len=pvm_const.MaxLen.VDISK_NAME)
|
|
@ -122,6 +122,7 @@ class TestSSPDiskAdapter(test.NoDBTestCase):
|
|||
|
||||
def test_capabilities(self):
|
||||
self.assertTrue(self.ssp_drv.capabilities.get('shared_storage'))
|
||||
self.assertFalse(self.ssp_drv.capabilities.get('has_imagecache'))
|
||||
self.assertTrue(self.ssp_drv.capabilities.get('snapshot'))
|
||||
|
||||
@mock.patch('pypowervm.util.get_req_path_uuid', autospec=True)
|
||||
|
@ -164,7 +165,7 @@ class TestSSPDiskAdapter(test.NoDBTestCase):
|
|||
@mock.patch('pypowervm.util.sanitize_file_name_for_api', autospec=True)
|
||||
@mock.patch('pypowervm.tasks.storage.crt_lu', autospec=True)
|
||||
@mock.patch('nova.image.api.API.download')
|
||||
@mock.patch('nova.virt.powervm.disk.ssp.IterableToFileAdapter',
|
||||
@mock.patch('nova.virt.powervm.disk.driver.IterableToFileAdapter',
|
||||
autospec=True)
|
||||
def test_create_disk_from_image(self, mock_it2f, mock_dl, mock_crt_lu,
|
||||
mock_san, mock_vuuid, mock_goru):
|
||||
|
@ -342,26 +343,6 @@ class TestSSPDiskAdapter(test.NoDBTestCase):
|
|||
mock_disk_name.assert_called_once_with('disk_type', 'instance')
|
||||
mock_gen_match.assert_called_with(pvm_stg.LU, names=['disk_name'])
|
||||
|
||||
@mock.patch("pypowervm.util.sanitize_file_name_for_api", autospec=True)
|
||||
def test_get_disk_name(self, mock_san):
|
||||
inst = mock.Mock()
|
||||
inst.configure_mock(name='a_name_that_is_longer_than_eight',
|
||||
uuid='01234567-abcd-abcd-abcd-123412341234')
|
||||
|
||||
# Long
|
||||
self.assertEqual(mock_san.return_value,
|
||||
self.ssp_drv._get_disk_name('type', inst))
|
||||
mock_san.assert_called_with(inst.name, prefix='type_',
|
||||
max_len=pvm_const.MaxLen.FILENAME_DEFAULT)
|
||||
|
||||
mock_san.reset_mock()
|
||||
|
||||
# Short
|
||||
self.assertEqual(mock_san.return_value,
|
||||
self.ssp_drv._get_disk_name('type', inst, short=True))
|
||||
mock_san.assert_called_with('a_name_t_0123', prefix='t_',
|
||||
max_len=pvm_const.MaxLen.VDISK_NAME)
|
||||
|
||||
@mock.patch('nova.virt.powervm.disk.ssp.SSPDiskAdapter.'
|
||||
'_vios_uuids', new_callable=mock.PropertyMock)
|
||||
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper', autospec=True)
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
# Copyright 2013 OpenStack Foundation
|
||||
# Copyright 2015, 2018 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 oslo_log.log as logging
|
||||
import pypowervm.const as pvm_const
|
||||
import pypowervm.tasks.scsi_mapper as tsk_map
|
||||
import pypowervm.util as pvm_u
|
||||
import pypowervm.wrappers.virtual_io_server as pvm_vios
|
||||
import six
|
||||
|
||||
from nova import exception
|
||||
from nova.virt.powervm import mgmt
|
||||
from nova.virt.powervm import vm
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DiskType(object):
|
||||
BOOT = 'boot'
|
||||
IMAGE = 'image'
|
||||
|
||||
|
||||
class IterableToFileAdapter(object):
|
||||
"""A degenerate file-like so that an iterable can be read like a file.
|
||||
|
||||
The Glance client returns an iterable, but PowerVM requires a file. This
|
||||
is the adapter between the two.
|
||||
|
||||
Taken from xenapi/image/apis.py
|
||||
"""
|
||||
|
||||
def __init__(self, iterable):
|
||||
self.iterator = iterable.__iter__()
|
||||
self.remaining_data = ''
|
||||
|
||||
def read(self, size):
|
||||
chunk = self.remaining_data
|
||||
try:
|
||||
while not chunk:
|
||||
chunk = next(self.iterator)
|
||||
except StopIteration:
|
||||
return ''
|
||||
return_value = chunk[0:size]
|
||||
self.remaining_data = chunk[size:]
|
||||
return return_value
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class DiskAdapter(object):
|
||||
|
||||
capabilities = {
|
||||
'shared_storage': False,
|
||||
'has_imagecache': False,
|
||||
'snapshot': False,
|
||||
}
|
||||
|
||||
def __init__(self, adapter, host_uuid):
|
||||
"""Initialize the DiskAdapter.
|
||||
|
||||
:param adapter: The pypowervm adapter.
|
||||
:param host_uuid: The UUID of the PowerVM host.
|
||||
"""
|
||||
self._adapter = adapter
|
||||
self._host_uuid = host_uuid
|
||||
self.mp_uuid = mgmt.mgmt_uuid(self._adapter)
|
||||
|
||||
@abc.abstractproperty
|
||||
def _vios_uuids(self):
|
||||
"""List the UUIDs of the Virtual I/O Servers hosting the storage."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def _disk_match_func(self, disk_type, instance):
|
||||
"""Return a matching function to locate the disk for an instance.
|
||||
|
||||
:param disk_type: One of the DiskType enum values.
|
||||
:param instance: The instance whose disk is to be found.
|
||||
:return: Callable suitable for the match_func parameter of the
|
||||
pypowervm.tasks.scsi_mapper.find_maps method, with the
|
||||
following specification:
|
||||
def match_func(storage_elem)
|
||||
param storage_elem: A backing storage element wrapper (VOpt,
|
||||
VDisk, PV, or LU) to be analyzed.
|
||||
return: True if the storage_elem's mapping should be included;
|
||||
False otherwise.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_bootdisk_path(self, instance, vios_uuid):
|
||||
"""Find scsi mappings on given VIOS for the instance.
|
||||
|
||||
This method finds all scsi mappings on a given vios that are associated
|
||||
with the instance and disk_type.
|
||||
|
||||
:param instance: nova.objects.instance.Instance object owning the
|
||||
requested disk.
|
||||
:param vios_uuid: PowerVM UUID of the VIOS to search for mappings.
|
||||
:return: Iterator of scsi mappings that are associated with the
|
||||
instance and disk_type or None.
|
||||
"""
|
||||
vm_uuid = vm.get_pvm_uuid(instance)
|
||||
match_func = self._disk_match_func(DiskType.BOOT, instance)
|
||||
vios_wrap = pvm_vios.VIOS.get(self._adapter, uuid=vios_uuid,
|
||||
xag=[pvm_const.XAG.VIO_SMAP])
|
||||
maps = tsk_map.find_maps(vios_wrap.scsi_mappings,
|
||||
client_lpar_id=vm_uuid, match_func=match_func)
|
||||
if maps:
|
||||
return maps[0].server_adapter.backing_dev_name
|
||||
return None
|
||||
|
||||
def _get_bootdisk_iter(self, instance):
|
||||
"""Return an iterator of (storage_elem, VIOS) tuples for the instance.
|
||||
|
||||
This method returns an iterator of (storage_elem, VIOS) tuples, where
|
||||
storage_element is a pypowervm storage element wrapper associated with
|
||||
the instance boot disk and VIOS is the wrapper of the Virtual I/O
|
||||
server owning that storage element.
|
||||
|
||||
:param instance: nova.objects.instance.Instance object owning the
|
||||
requested disk.
|
||||
:return: Iterator of tuples of (storage_elem, VIOS).
|
||||
"""
|
||||
lpar_wrap = vm.get_instance_wrapper(self._adapter, instance)
|
||||
match_func = self._disk_match_func(DiskType.BOOT, instance)
|
||||
for vios_uuid in self._vios_uuids:
|
||||
vios_wrap = pvm_vios.VIOS.get(
|
||||
self._adapter, uuid=vios_uuid, xag=[pvm_const.XAG.VIO_SMAP])
|
||||
for scsi_map in tsk_map.find_maps(
|
||||
vios_wrap.scsi_mappings, client_lpar_id=lpar_wrap.id,
|
||||
match_func=match_func):
|
||||
yield scsi_map.backing_storage, vios_wrap
|
||||
|
||||
def connect_instance_disk_to_mgmt(self, instance):
|
||||
"""Connect an instance's boot disk to the management partition.
|
||||
|
||||
:param instance: The instance whose boot disk is to be mapped.
|
||||
:return stg_elem: The storage element (LU, VDisk, etc.) that was mapped
|
||||
:return vios: The EntryWrapper of the VIOS from which the mapping was
|
||||
made.
|
||||
:raise InstanceDiskMappingFailed: If the mapping could not be done.
|
||||
"""
|
||||
for stg_elem, vios in self._get_bootdisk_iter(instance):
|
||||
msg_args = {'disk_name': stg_elem.name, 'vios_name': vios.name}
|
||||
|
||||
# Create a new mapping. NOTE: If there's an existing mapping on
|
||||
# the other VIOS but not this one, we'll create a second mapping
|
||||
# here. It would take an extreme sequence of events to get to that
|
||||
# point, and the second mapping would be harmless anyway. The
|
||||
# alternative would be always checking all VIOSes for existing
|
||||
# mappings, which increases the response time of the common case by
|
||||
# an entire GET of VIOS+VIO_SMAP.
|
||||
LOG.debug("Mapping boot disk %(disk_name)s to the management "
|
||||
"partition from Virtual I/O Server %(vios_name)s.",
|
||||
msg_args, instance=instance)
|
||||
try:
|
||||
tsk_map.add_vscsi_mapping(self._host_uuid, vios, self.mp_uuid,
|
||||
stg_elem)
|
||||
# If that worked, we're done. add_vscsi_mapping logged.
|
||||
return stg_elem, vios
|
||||
except Exception:
|
||||
LOG.exception("Failed to map boot disk %(disk_name)s to the "
|
||||
"management partition from Virtual I/O Server "
|
||||
"%(vios_name)s.", msg_args, instance=instance)
|
||||
# Try the next hit, if available.
|
||||
# We either didn't find the boot dev, or failed all attempts to map it.
|
||||
raise exception.InstanceDiskMappingFailed(instance_name=instance.name)
|
||||
|
||||
@abc.abstractmethod
|
||||
def disconnect_disk_from_mgmt(self, vios_uuid, disk_name):
|
||||
"""Disconnect a disk from the management partition.
|
||||
|
||||
:param vios_uuid: The UUID of the Virtual I/O Server serving the
|
||||
mapping.
|
||||
:param disk_name: The name of the disk to unmap.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractproperty
|
||||
def capacity(self):
|
||||
"""Capacity of the storage in gigabytes.
|
||||
|
||||
Default is to make the capacity arbitrarily large.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractproperty
|
||||
def capacity_used(self):
|
||||
"""Capacity of the storage in gigabytes that is used.
|
||||
|
||||
Default is to say none of it is used.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
def _get_disk_name(disk_type, instance, short=False):
|
||||
"""Generate a name for a virtual disk associated with an instance.
|
||||
|
||||
:param disk_type: One of the DiskType enum values.
|
||||
:param instance: The instance for which the disk is to be created.
|
||||
:param short: If True, the generated name will be limited to 15
|
||||
characters (the limit for virtual disk). If False, it
|
||||
will be limited by the API (79 characters currently).
|
||||
:return: The sanitized file name for the disk.
|
||||
"""
|
||||
prefix = '%s_' % (disk_type[0] if short else disk_type)
|
||||
base = ('%s_%s' % (instance.name[:8], instance.uuid[:4]) if short
|
||||
else instance.name)
|
||||
return pvm_u.sanitize_file_name_for_api(
|
||||
base, prefix=prefix, max_len=pvm_const.MaxLen.VDISK_NAME if short
|
||||
else pvm_const.MaxLen.FILENAME_DEFAULT)
|
||||
|
||||
@abc.abstractmethod
|
||||
def detach_disk(self, instance):
|
||||
"""Detaches the storage adapters from the image disk.
|
||||
|
||||
:param instance: instance to detach the image for.
|
||||
:return: A list of all the backing storage elements that were
|
||||
detached from the I/O Server and VM.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_disks(self, storage_elems):
|
||||
"""Removes the disks specified by the mappings.
|
||||
|
||||
:param storage_elems: A list of the storage elements that are to be
|
||||
deleted. Derived from the return value from
|
||||
detach_disk.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_disk_from_image(self, context, instance, image_meta):
|
||||
"""Creates a disk and copies the specified image to it.
|
||||
|
||||
Cleans up created disk if an error occurs.
|
||||
:param context: nova context used to retrieve image from glance
|
||||
:param instance: instance to create the disk for.
|
||||
:param image_meta: nova.objects.ImageMeta object with the metadata of
|
||||
the image of the instance.
|
||||
:return: The backing pypowervm storage object that was created.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def attach_disk(self, instance, disk_info, stg_ftsk):
|
||||
"""Attaches the disk image to the Virtual Machine.
|
||||
|
||||
:param instance: nova instance to attach the disk to.
|
||||
:param disk_info: The pypowervm storage element returned from
|
||||
create_disk_from_image. Ex. VOptMedia, VDisk, LU,
|
||||
or PV.
|
||||
:param stg_ftsk: (Optional) The pypowervm transaction FeedTask for the
|
||||
I/O Operations. If provided, the Virtual I/O Server
|
||||
mapping updates will be added to the FeedTask. This
|
||||
defers the updates to some later point in time. If
|
||||
the FeedTask is not provided, the updates will be run
|
||||
immediately when this method is executed.
|
||||
"""
|
||||
raise NotImplementedError()
|
|
@ -24,50 +24,18 @@ from pypowervm.tasks import storage as tsk_stg
|
|||
import pypowervm.util as pvm_u
|
||||
import pypowervm.wrappers.cluster as pvm_clust
|
||||
import pypowervm.wrappers.storage as pvm_stg
|
||||
import pypowervm.wrappers.virtual_io_server as pvm_vios
|
||||
|
||||
from nova import exception
|
||||
from nova import image
|
||||
from nova.virt.powervm import mgmt
|
||||
from nova.virt.powervm.disk import driver as disk_drv
|
||||
from nova.virt.powervm import vm
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
IMAGE_API = image.API()
|
||||
|
||||
|
||||
class DiskType(object):
|
||||
BOOT = 'boot'
|
||||
IMAGE = 'image'
|
||||
|
||||
|
||||
class IterableToFileAdapter(object):
|
||||
"""A degenerate file-like so that an iterable can be read like a file.
|
||||
|
||||
The Glance client returns an iterable, but PowerVM requires a file. This
|
||||
is the adapter between the two.
|
||||
|
||||
Taken from xenapi/image/apis.py
|
||||
"""
|
||||
|
||||
def __init__(self, iterable):
|
||||
self.iterator = iterable.__iter__()
|
||||
self.remaining_data = ''
|
||||
|
||||
def read(self, size):
|
||||
chunk = self.remaining_data
|
||||
try:
|
||||
while not chunk:
|
||||
chunk = next(self.iterator)
|
||||
except StopIteration:
|
||||
return ''
|
||||
return_value = chunk[0:size]
|
||||
self.remaining_data = chunk[size:]
|
||||
return return_value
|
||||
|
||||
|
||||
class SSPDiskAdapter(object):
|
||||
class SSPDiskAdapter(disk_drv.DiskAdapter):
|
||||
"""Provides a disk adapter for Shared Storage Pools.
|
||||
|
||||
Shared Storage Pools are a clustered file system technology that can link
|
||||
|
@ -79,6 +47,13 @@ class SSPDiskAdapter(object):
|
|||
|
||||
capabilities = {
|
||||
'shared_storage': True,
|
||||
# NOTE(efried): Whereas the SSP disk driver definitely does image
|
||||
# caching, it's not through the nova.virt.imagecache.ImageCacheManager
|
||||
# API. Setting `has_imagecache` to True here would have the side
|
||||
# effect of having a periodic task try to call this class's
|
||||
# manage_image_cache method (not implemented here; and a no-op in the
|
||||
# superclass) which would be harmless, but unnecessary.
|
||||
'has_imagecache': False,
|
||||
'snapshot': True,
|
||||
}
|
||||
|
||||
|
@ -88,9 +63,8 @@ class SSPDiskAdapter(object):
|
|||
:param adapter: pypowervm.adapter.Adapter for the PowerVM REST API.
|
||||
:param host_uuid: PowerVM UUID of the managed system.
|
||||
"""
|
||||
self._adapter = adapter
|
||||
self._host_uuid = host_uuid
|
||||
self.mp_uuid = mgmt.mgmt_uuid(self._adapter)
|
||||
super(SSPDiskAdapter, self).__init__(adapter, host_uuid)
|
||||
|
||||
try:
|
||||
self._clust = pvm_clust.Cluster.get(self._adapter)[0]
|
||||
self._ssp = pvm_stg.SSP.get_by_href(
|
||||
|
@ -192,14 +166,14 @@ class SSPDiskAdapter(object):
|
|||
|
||||
image_lu = tsk_cs.get_or_upload_image_lu(
|
||||
self._tier, pvm_u.sanitize_file_name_for_api(
|
||||
image_meta.name, prefix=DiskType.IMAGE + '_',
|
||||
image_meta.name, prefix=disk_drv.DiskType.IMAGE + '_',
|
||||
suffix='_' + image_meta.checksum),
|
||||
random.choice(self._vios_uuids), IterableToFileAdapter(
|
||||
random.choice(self._vios_uuids), disk_drv.IterableToFileAdapter(
|
||||
IMAGE_API.download(context, image_meta.id)), image_meta.size,
|
||||
upload_type=tsk_stg.UploadType.IO_STREAM)
|
||||
|
||||
boot_lu_name = pvm_u.sanitize_file_name_for_api(
|
||||
instance.name, prefix=DiskType.BOOT + '_')
|
||||
instance.name, prefix=disk_drv.DiskType.BOOT + '_')
|
||||
|
||||
LOG.info('SSP: Disk name is %s', boot_lu_name, instance=instance)
|
||||
|
||||
|
@ -257,59 +231,6 @@ class SSPDiskAdapter(object):
|
|||
ret.append(n.vios_uuid)
|
||||
return ret
|
||||
|
||||
def get_bootdisk_path(self, instance, vios_uuid):
|
||||
"""Get the local path for an instance's boot disk.
|
||||
|
||||
:param instance: nova.objects.instance.Instance object owning the
|
||||
requested disk.
|
||||
:param vios_uuid: PowerVM UUID of the VIOS to search for mappings.
|
||||
:return: Local path for instance's boot disk.
|
||||
"""
|
||||
vm_uuid = vm.get_pvm_uuid(instance)
|
||||
match_func = self._disk_match_func(DiskType.BOOT, instance)
|
||||
vios_wrap = pvm_vios.VIOS.get(self._adapter, uuid=vios_uuid,
|
||||
xag=[pvm_const.XAG.VIO_SMAP])
|
||||
maps = tsk_map.find_maps(vios_wrap.scsi_mappings,
|
||||
client_lpar_id=vm_uuid, match_func=match_func)
|
||||
if maps:
|
||||
return maps[0].server_adapter.backing_dev_name
|
||||
return None
|
||||
|
||||
def connect_instance_disk_to_mgmt(self, instance):
|
||||
"""Connect an instance's boot disk to the management partition.
|
||||
|
||||
:param instance: The instance whose boot disk is to be mapped.
|
||||
:return stg_elem: The storage element (LU, VDisk, etc.) that was mapped
|
||||
:return vios: The EntryWrapper of the VIOS from which the mapping was
|
||||
made.
|
||||
:raise InstanceDiskMappingFailed: If the mapping could not be done.
|
||||
"""
|
||||
for stg_elem, vios in self._get_bootdisk_iter(instance):
|
||||
msg_args = {'disk_name': stg_elem.name, 'vios_name': vios.name}
|
||||
|
||||
# Create a new mapping. NOTE: If there's an existing mapping on
|
||||
# the other VIOS but not this one, we'll create a second mapping
|
||||
# here. It would take an extreme sequence of events to get to that
|
||||
# point, and the second mapping would be harmless anyway. The
|
||||
# alternative would be always checking all VIOSes for existing
|
||||
# mappings, which increases the response time of the common case by
|
||||
# an entire GET of VIOS+VIO_SMAP.
|
||||
LOG.debug("Mapping boot disk %(disk_name)s to the management "
|
||||
"partition from Virtual I/O Server %(vios_name)s.",
|
||||
msg_args, instance=instance)
|
||||
try:
|
||||
tsk_map.add_vscsi_mapping(self._host_uuid, vios, self.mp_uuid,
|
||||
stg_elem)
|
||||
# If that worked, we're done. add_vscsi_mapping logged.
|
||||
return stg_elem, vios
|
||||
except pvm_exc.Error:
|
||||
LOG.exception("Failed to map boot disk %(disk_name)s to the "
|
||||
"management partition from Virtual I/O Server "
|
||||
"%(vios_name)s.", msg_args, instance=instance)
|
||||
# Try the next hit, if available.
|
||||
# We either didn't find the boot dev, or failed all attempts to map it.
|
||||
raise exception.InstanceDiskMappingFailed(instance_name=instance.name)
|
||||
|
||||
def disconnect_disk_from_mgmt(self, vios_uuid, disk_name):
|
||||
"""Disconnect a disk from the management partition.
|
||||
|
||||
|
@ -335,42 +256,3 @@ class SSPDiskAdapter(object):
|
|||
"""
|
||||
disk_name = SSPDiskAdapter._get_disk_name(disk_type, instance)
|
||||
return tsk_map.gen_match_func(pvm_stg.LU, names=[disk_name])
|
||||
|
||||
@staticmethod
|
||||
def _get_disk_name(disk_type, instance, short=False):
|
||||
"""Generate a name for a virtual disk associated with an instance.
|
||||
|
||||
:param disk_type: One of the DiskType enum values.
|
||||
:param instance: The instance for which the disk is to be created.
|
||||
:param short: If True, the generated name will be limited to 15
|
||||
characters (the limit for virtual disk). If False, it
|
||||
will be limited by the API (79 characters currently).
|
||||
:return: The sanitized file name for the disk.
|
||||
"""
|
||||
prefix = '%s_' % (disk_type[0] if short else disk_type)
|
||||
base = ('%s_%s' % (instance.name[:8], instance.uuid[:4]) if short
|
||||
else instance.name)
|
||||
return pvm_u.sanitize_file_name_for_api(
|
||||
base, prefix=prefix, max_len=pvm_const.MaxLen.VDISK_NAME if short
|
||||
else pvm_const.MaxLen.FILENAME_DEFAULT)
|
||||
|
||||
def _get_bootdisk_iter(self, instance):
|
||||
"""Return an iterator of (storage_elem, VIOS) tuples for the instance.
|
||||
|
||||
storage_elem is a pypowervm storage element wrapper associated with
|
||||
the instance boot disk and VIOS is the wrapper of the Virtual I/O
|
||||
server owning that storage element.
|
||||
|
||||
:param instance: nova.objects.instance.Instance object owning the
|
||||
requested disk.
|
||||
:return: Iterator of tuples of (storage_elem, VIOS).
|
||||
"""
|
||||
lpar_wrap = vm.get_instance_wrapper(self._adapter, instance)
|
||||
match_func = self._disk_match_func(DiskType.BOOT, instance)
|
||||
for vios_uuid in self._vios_uuids:
|
||||
vios_wrap = pvm_vios.VIOS.get(
|
||||
self._adapter, uuid=vios_uuid, xag=[pvm_const.XAG.VIO_SMAP])
|
||||
for scsi_map in tsk_map.find_maps(
|
||||
vios_wrap.scsi_mappings, client_lpar_id=lpar_wrap.id,
|
||||
match_func=match_func):
|
||||
yield scsi_map.backing_storage, vios_wrap
|
||||
|
|
Loading…
Reference in New Issue