Merge "Disk Driver: instance_disk_iter"
This commit is contained in:
commit
12f363bf38
|
@ -14,6 +14,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
|
@ -204,6 +206,41 @@ class TestLocalDisk(test.TestCase):
|
|||
self.assertEqual(1, resp.update.call_count)
|
||||
self.assertEqual(vdisk.capacity, 1000)
|
||||
|
||||
@mock.patch('pypowervm.wrappers.storage.VG')
|
||||
def test_instance_disk_iter(self, mock_vg):
|
||||
local = self.get_ls(self.apt)
|
||||
inst = mock.Mock()
|
||||
inst.name = 'Name Of Instance'
|
||||
inst.uuid = 'd5065c2c-ac43-3fa6-af32-ea84a3960291'
|
||||
lpar_wrap = mock.Mock()
|
||||
lpar_wrap.id = 2
|
||||
vios1 = pvm_vios.VIOS.wrap(self.vio_to_vg)
|
||||
vios2 = copy.deepcopy(vios1)
|
||||
vios1.scsi_mappings[0].backing_storage.name = 'b_Name_Of__d506'
|
||||
|
||||
# Good path
|
||||
self.apt.read.return_value = vios1.entry
|
||||
for vdisk, vios in local.instance_disk_iter(inst, lpar_wrap=lpar_wrap):
|
||||
self.assertEqual('0300025d4a00007a000000014b36d9deaf.1',
|
||||
vdisk.udid)
|
||||
self.assertEqual('3443DB77-AED1-47ED-9AA5-3DB9C6CF7089', vios.uuid)
|
||||
self.assertEqual(1, self.apt.read.call_count)
|
||||
|
||||
# Not found because no storage of that name
|
||||
self.apt.reset_mock()
|
||||
self.apt.read.return_value = vios2.entry
|
||||
for vdisk, vios in local.instance_disk_iter(inst, lpar_wrap=lpar_wrap):
|
||||
self.fail()
|
||||
self.assertEqual(1, self.apt.read.call_count)
|
||||
|
||||
# Not found because LPAR ID doesn't match
|
||||
self.apt.reset_mock()
|
||||
self.apt.read.return_value = vios1.entry
|
||||
lpar_wrap.id = 3
|
||||
for vdisk, vios in local.instance_disk_iter(inst, lpar_wrap=lpar_wrap):
|
||||
self.fail()
|
||||
self.assertEqual(1, self.apt.read.call_count)
|
||||
|
||||
|
||||
class TestLocalDiskFindVG(test.TestCase):
|
||||
"""Test in separate class for the static loading of the VG.
|
||||
|
@ -246,7 +283,7 @@ class TestLocalDiskFindVG(test.TestCase):
|
|||
storage = ld.LocalStorage({'adapter': self.apt,
|
||||
'host_uuid': 'host_uuid'})
|
||||
|
||||
# Make sure the uuid's match
|
||||
# Make sure the uuids match
|
||||
self.assertEqual('d5065c2c-ac43-3fa6-af32-ea84a3960291',
|
||||
storage.vg_uuid)
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
|
@ -25,6 +27,7 @@ import pypowervm.entities as pvm_ent
|
|||
from pypowervm.tests.wrappers.util import pvmhttp
|
||||
from pypowervm.wrappers import cluster as pvm_clust
|
||||
from pypowervm.wrappers import storage as pvm_stg
|
||||
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
||||
|
||||
from nova_powervm.tests.virt.powervm import fixtures as fx
|
||||
from nova_powervm.virt.powervm.disk import ssp
|
||||
|
@ -434,3 +437,68 @@ class TestSSPDiskAdapter(test.TestCase):
|
|||
not_same = {'ssp_uuid': 'uuid value not the same'}
|
||||
self.assertFalse(
|
||||
ssp_stor.check_instance_shared_storage_remote('context', not_same))
|
||||
|
||||
def test_instance_disk_iter(self):
|
||||
ssp_stor = self._get_ssp_stor()
|
||||
inst = mock.Mock()
|
||||
inst.name = 'my-instance-name'
|
||||
lpar_wrap = mock.Mock()
|
||||
lpar_wrap.id = 4
|
||||
# Build mock VIOS Wrappers as the returns from VIOS.wrap.
|
||||
# vios1 and vios2 will both have the mapping for client ID 4 and LU
|
||||
# named boot_my_instance_name.
|
||||
vios1 = pvm_vios.VIOS.wrap(pvmhttp.load_pvm_resp(
|
||||
'fake_vios_ssp_npiv.txt', adapter=self.apt).get_response())
|
||||
vios1.scsi_mappings[3].backing_storage._name('boot_my_instance_name')
|
||||
resp1 = self._bld_resp(entry_or_list=vios1.entry)
|
||||
vios2 = copy.deepcopy(vios1)
|
||||
# Rename vios2 so we can tell the difference:
|
||||
vios2.name = 'vios2'
|
||||
resp2 = self._bld_resp(entry_or_list=vios2.entry)
|
||||
# vios3 will not have the mapping
|
||||
vios3 = pvm_vios.VIOS.wrap(pvmhttp.load_pvm_resp(
|
||||
'fake_vios_ssp_npiv.txt', adapter=self.apt).get_response())
|
||||
vios3.name = 'vios3'
|
||||
resp3 = self._bld_resp(entry_or_list=vios3.entry)
|
||||
|
||||
# Test with two VIOSes, both of which contain the mapping
|
||||
self.apt.read.side_effect = [resp1, resp2]
|
||||
count = 0
|
||||
for lu, vios in ssp_stor.instance_disk_iter(inst, lpar_wrap=lpar_wrap):
|
||||
count += 1
|
||||
self.assertEqual('274d7bb790666211e3bc1a00006cae8b01ac18997ab9bc23'
|
||||
'fb24756e9713a93f90', lu.udid)
|
||||
self.assertEqual('vios1_181.68' if count == 1 else 'vios2',
|
||||
vios.name)
|
||||
self.assertEqual(2, count)
|
||||
self.assertEqual(2, self.apt.read.call_count)
|
||||
|
||||
# Same, but prove that breaking out of the loop early avoids the second
|
||||
# Adapter.read call
|
||||
self.apt.reset_mock()
|
||||
self.apt.read.side_effect = [resp1, resp2]
|
||||
for lu, vios in ssp_stor.instance_disk_iter(inst, lpar_wrap=lpar_wrap):
|
||||
self.assertEqual('274d7bb790666211e3bc1a00006cae8b01ac18997ab9bc23'
|
||||
'fb24756e9713a93f90', lu.udid)
|
||||
self.assertEqual('vios1_181.68', vios.name)
|
||||
break
|
||||
self.assertEqual(1, self.apt.read.call_count)
|
||||
|
||||
# Now the first VIOS doesn't have the mapping, but the second does
|
||||
self.apt.reset_mock()
|
||||
self.apt.read.side_effect = [resp3, resp2]
|
||||
count = 0
|
||||
for lu, vios in ssp_stor.instance_disk_iter(inst, lpar_wrap=lpar_wrap):
|
||||
count += 1
|
||||
self.assertEqual('274d7bb790666211e3bc1a00006cae8b01ac18997ab9bc23'
|
||||
'fb24756e9713a93f90', lu.udid)
|
||||
self.assertEqual('vios2', vios.name)
|
||||
self.assertEqual(1, count)
|
||||
self.assertEqual(2, self.apt.read.call_count)
|
||||
|
||||
# No hits
|
||||
self.apt.reset_mock()
|
||||
self.apt.read.side_effect = [resp3, resp3]
|
||||
for lu, vios in ssp_stor.instance_disk_iter(inst, lpar_wrap=lpar_wrap):
|
||||
self.fail()
|
||||
self.assertEqual(2, self.apt.read.call_count)
|
||||
|
|
|
@ -21,7 +21,11 @@ from oslo_utils import units
|
|||
import six
|
||||
|
||||
from nova import image
|
||||
import pypowervm.tasks.scsi_mapper as tsk_map
|
||||
import pypowervm.util as pvm_util
|
||||
import pypowervm.wrappers.virtual_io_server as pvm_vios
|
||||
|
||||
from nova_powervm.virt.powervm import vm
|
||||
|
||||
|
||||
class DiskType(object):
|
||||
|
@ -66,6 +70,57 @@ class DiskAdapter(object):
|
|||
self._connection = connection
|
||||
self.image_api = image.API()
|
||||
|
||||
@property
|
||||
def vios_uuids(self):
|
||||
"""List the UUIDs of the Virtual I/O Servers hosting the storage."""
|
||||
raise NotImplementedError()
|
||||
|
||||
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 instance_disk_iter(self, instance, disk_type=DiskType.BOOT,
|
||||
lpar_wrap=None):
|
||||
"""Return the instance's storage element wrapper of the specified type.
|
||||
|
||||
:param instance: nova.objects.instance.Instance object owning the
|
||||
requested disk.
|
||||
:param disk_type: The type of disk to find, one of the DiskType enum
|
||||
values.
|
||||
:param lpar_wrap: pypowervm.wrappers.logical_partition.LPAR
|
||||
corresponding to the instance. If not specified, it
|
||||
will be retrieved; i.e. specify this parameter to
|
||||
save on REST calls.
|
||||
:return: Iterator of tuples of (storage_elem, VIOS), where storage_elem
|
||||
is a storage element wrapper (pypowervm.wrappers.storage.VOpt,
|
||||
VDisk, PV, or LU) associated with the instance; and VIOS is
|
||||
the wrapper of the Virtual I/O Server owning that storage
|
||||
element.
|
||||
"""
|
||||
if lpar_wrap is None:
|
||||
lpar_wrap = vm.get_instance_wrapper(self.adapter, instance,
|
||||
self.host_uuid)
|
||||
match_func = self.disk_match_func(disk_type, instance)
|
||||
for vios_uuid in self.vios_uuids:
|
||||
vios_wrap = pvm_vios.VIOS.wrap(self.adapter.read(
|
||||
pvm_vios.VIOS.schema_type, root_id=vios_uuid,
|
||||
xag=pvm_vios.VIOS.xags.SCSI_MAPPING))
|
||||
for scsi_map in tsk_map.find_maps(
|
||||
vios_wrap.scsi_mappings, lpar_wrap.id, match_func):
|
||||
yield scsi_map.backing_storage, vios_wrap
|
||||
|
||||
@property
|
||||
def capacity(self):
|
||||
"""Capacity of the storage in gigabytes
|
||||
|
|
|
@ -66,10 +66,35 @@ class LocalStorage(disk_dvr.DiskAdapter):
|
|||
|
||||
# Query to get the Volume Group UUID
|
||||
self.vg_name = CONF.volume_group_name
|
||||
self.vios_uuid, self.vg_uuid = self._get_vg_uuid(self.vg_name)
|
||||
self._vios_uuid, self.vg_uuid = self._get_vg_uuid(self.vg_name)
|
||||
LOG.info(_LI("Local Storage driver initialized: volume group: '%s'"),
|
||||
self.vg_name)
|
||||
|
||||
@property
|
||||
def vios_uuids(self):
|
||||
"""List the UUIDs of the Virtual I/O Servers hosting the storage.
|
||||
|
||||
For localdisk, there's only one.
|
||||
"""
|
||||
return [self._vios_uuid]
|
||||
|
||||
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.
|
||||
"""
|
||||
disk_name = self._get_disk_name(disk_type, instance, short=True)
|
||||
return tsk_map.gen_match_func(pvm_stg.VDisk, names=[disk_name])
|
||||
|
||||
@property
|
||||
def capacity(self):
|
||||
"""Capacity of the storage in gigabytes."""
|
||||
|
@ -131,7 +156,7 @@ class LocalStorage(disk_dvr.DiskAdapter):
|
|||
disconnected from the I/O Server and VM.
|
||||
"""
|
||||
partition_id = vm.get_vm_id(self.adapter, lpar_uuid)
|
||||
return tsk_map.remove_vdisk_mapping(self.adapter, self.vios_uuid,
|
||||
return tsk_map.remove_vdisk_mapping(self.adapter, self._vios_uuid,
|
||||
partition_id,
|
||||
disk_prefixes=disk_type)
|
||||
|
||||
|
@ -164,7 +189,7 @@ class LocalStorage(disk_dvr.DiskAdapter):
|
|||
# If the image is bigger than disk, API should make the disk big
|
||||
# enough to support the image (up to 1 Gb boundary).
|
||||
vdisk, f_wrap = tsk_stg.upload_new_vdisk(
|
||||
self.adapter, self.vios_uuid, self.vg_uuid, stream, vol_name,
|
||||
self.adapter, self._vios_uuid, self.vg_uuid, stream, vol_name,
|
||||
image['size'], d_size=disk_bytes)
|
||||
|
||||
return vdisk
|
||||
|
@ -180,7 +205,7 @@ class LocalStorage(disk_dvr.DiskAdapter):
|
|||
:param: lpar_uuid: The pypowervm UUID that corresponds to the VM.
|
||||
"""
|
||||
# Add the mapping to the VIOS
|
||||
tsk_map.add_vscsi_mapping(self.host_uuid, self.vios_uuid, lpar_uuid,
|
||||
tsk_map.add_vscsi_mapping(self.host_uuid, self._vios_uuid, lpar_uuid,
|
||||
disk_info)
|
||||
|
||||
def extend_disk(self, context, instance, disk_info, size):
|
||||
|
@ -259,7 +284,7 @@ class LocalStorage(disk_dvr.DiskAdapter):
|
|||
|
||||
def _get_vg(self):
|
||||
vg_rsp = self.adapter.read(
|
||||
pvm_vios.VIOS.schema_type, root_id=self.vios_uuid,
|
||||
pvm_vios.VIOS.schema_type, root_id=self._vios_uuid,
|
||||
child_type=pvm_stg.VG.schema_type, child_id=self.vg_uuid)
|
||||
return vg_rsp
|
||||
|
||||
|
|
|
@ -389,3 +389,20 @@ class SSPDiskAdapter(disk_drv.DiskAdapter):
|
|||
:return: A single VIOS UUID string.
|
||||
"""
|
||||
return random.choice(self.vios_uuids)
|
||||
|
||||
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.
|
||||
"""
|
||||
disk_name = self._get_disk_name(disk_type, instance)
|
||||
return tsk_map.gen_match_func(pvm_stg.LU, names=[disk_name])
|
||||
|
|
Loading…
Reference in New Issue