Merge "Disk Driver: instance_disk_iter"

This commit is contained in:
Jenkins 2015-05-27 13:46:42 +00:00 committed by Gerrit Code Review
commit 12f363bf38
5 changed files with 208 additions and 6 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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])