Build vfc mappings for migration with vios slot collocation
The migration data on the source sets information about the slots which are collocated on a VIOS. This is used along with the fabric port information on the destination host to make sure the client slots are maintained on the same VIOS. Change-Id: Iddb4a535b67a7a157a2ac4be97a3ce028fed1696 Closes-Bug: #1623889
This commit is contained in:
@@ -19,6 +19,7 @@ import mock
|
||||
from nova.compute import task_states
|
||||
from oslo_serialization import jsonutils
|
||||
from pypowervm import const as pvm_const
|
||||
from pypowervm.tests.tasks import util as tju
|
||||
from pypowervm.tests import test_fixtures as pvm_fx
|
||||
from pypowervm.tests.test_utils import pvmhttp
|
||||
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
||||
@@ -29,6 +30,7 @@ from nova_powervm.virt.powervm import exception as exc
|
||||
from nova_powervm.virt.powervm.volume import npiv
|
||||
|
||||
VIOS_FEED = 'fake_vios_feed2.txt'
|
||||
VIOS_FEED_2 = 'fake_vios_feed.txt'
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@@ -516,12 +518,15 @@ class TestNPIVAdapter(test_vol.TestVolumeAdapter):
|
||||
[('11', 'AA BB'), ('22', 'CC DD')],
|
||||
[('33', 'EE FF')]]
|
||||
|
||||
vios_wraps = pvm_vios.VIOS.wrap(tju.load_file(VIOS_FEED))
|
||||
vios1_w = vios_wraps[0]
|
||||
|
||||
def mock_client_adpt(slot):
|
||||
return mock.Mock(client_adapter=mock.Mock(lpar_slot_num=slot))
|
||||
|
||||
mock_find_vios_for_vfc_wwpns.side_effect = [
|
||||
(None, mock_client_adpt(1)), (None, mock_client_adpt(2)),
|
||||
(None, mock_client_adpt(3))]
|
||||
(vios1_w, mock_client_adpt(1)), (vios1_w, mock_client_adpt(2)),
|
||||
(vios1_w, mock_client_adpt(3))]
|
||||
|
||||
# Execute the test
|
||||
mig_data = {}
|
||||
@@ -529,6 +534,65 @@ class TestNPIVAdapter(test_vol.TestVolumeAdapter):
|
||||
|
||||
self.assertEqual('[1, 2]', mig_data.get('src_npiv_fabric_slots_A'))
|
||||
self.assertEqual('[3]', mig_data.get('src_npiv_fabric_slots_B'))
|
||||
vios_peer_slots = jsonutils.loads(mig_data.get('src_vios_peer_slots'))
|
||||
self.assertItemsEqual([[1, 2, 3]], vios_peer_slots)
|
||||
# Ensure only string data is placed in the dict.
|
||||
for key in mig_data:
|
||||
self.assertEqual(str, type(mig_data[key]))
|
||||
|
||||
@mock.patch('pypowervm.tasks.vfc_mapper.find_vios_for_vfc_wwpns')
|
||||
@mock.patch('nova_powervm.virt.powervm.volume.npiv.NPIVVolumeAdapter.'
|
||||
'_get_fabric_meta')
|
||||
@mock.patch('nova_powervm.virt.powervm.volume.npiv.NPIVVolumeAdapter.'
|
||||
'_fabric_names')
|
||||
def test_pre_live_migration_on_source_dual_vios(
|
||||
self, mock_fabric_names, mock_get_fabric_meta,
|
||||
mock_find_vios_for_vfc_wwpns):
|
||||
mock_fabric_names.return_value = ['A', 'B']
|
||||
mock_get_fabric_meta.side_effect = [
|
||||
[('11', 'AA BB'), ('22', 'CC DD')],
|
||||
[('33', 'EE FF'), ('44', 'GG HH')]]
|
||||
|
||||
vios_wraps = pvm_vios.VIOS.wrap(tju.load_file(VIOS_FEED_2))
|
||||
vios1_w = vios_wraps[0]
|
||||
vios2_w = vios_wraps[1]
|
||||
|
||||
def mock_client_adpt(slot):
|
||||
return mock.Mock(client_adapter=mock.Mock(lpar_slot_num=slot))
|
||||
|
||||
mock_find_vios_for_vfc_wwpns.side_effect = [
|
||||
(vios1_w, mock_client_adpt(1)), (vios1_w, mock_client_adpt(2)),
|
||||
(vios2_w, mock_client_adpt(3)), (vios2_w, mock_client_adpt(4))]
|
||||
|
||||
# Execute the test
|
||||
mig_data = {}
|
||||
self.vol_drv.pre_live_migration_on_source(mig_data)
|
||||
|
||||
self.assertEqual('[1, 2]', mig_data.get('src_npiv_fabric_slots_A'))
|
||||
self.assertEqual('[3, 4]', mig_data.get('src_npiv_fabric_slots_B'))
|
||||
vios_peer_slots = jsonutils.loads(mig_data.get('src_vios_peer_slots'))
|
||||
self.assertItemsEqual([[1, 2], [3, 4]], vios_peer_slots)
|
||||
# Ensure only string data is placed in the dict.
|
||||
for key in mig_data:
|
||||
self.assertEqual(str, type(mig_data[key]))
|
||||
|
||||
# test unequal mapping across 2 VIOS
|
||||
mock_get_fabric_meta.side_effect = [
|
||||
[('11', 'AA BB'), ('22', 'CC DD')],
|
||||
[('33', 'EE FF')]]
|
||||
|
||||
mock_find_vios_for_vfc_wwpns.side_effect = [
|
||||
(vios1_w, mock_client_adpt(1)), (vios2_w, mock_client_adpt(2)),
|
||||
(vios1_w, mock_client_adpt(3))]
|
||||
|
||||
# Execute the test
|
||||
mig_data = {}
|
||||
self.vol_drv.pre_live_migration_on_source(mig_data)
|
||||
|
||||
self.assertEqual('[1, 2]', mig_data.get('src_npiv_fabric_slots_A'))
|
||||
self.assertEqual('[3]', mig_data.get('src_npiv_fabric_slots_B'))
|
||||
vios_peer_slots = jsonutils.loads(mig_data.get('src_vios_peer_slots'))
|
||||
self.assertItemsEqual([[1, 3], [2]], vios_peer_slots)
|
||||
# Ensure only string data is placed in the dict.
|
||||
for key in mig_data:
|
||||
self.assertEqual(str, type(mig_data[key]))
|
||||
@@ -537,7 +601,7 @@ class TestNPIVAdapter(test_vol.TestVolumeAdapter):
|
||||
'build_migration_mappings_for_fabric')
|
||||
@mock.patch('nova_powervm.virt.powervm.volume.npiv.NPIVVolumeAdapter.'
|
||||
'_fabric_names')
|
||||
def test_pre_live_migration_on_destination(
|
||||
def test_pre_live_migration_on_destination_legacy(
|
||||
self, mock_fabric_names, mock_build_mig_map):
|
||||
mock_fabric_names.return_value = ['A', 'B']
|
||||
|
||||
@@ -564,6 +628,29 @@ class TestNPIVAdapter(test_vol.TestVolumeAdapter):
|
||||
# Verify that on migration, the WWPNs are reversed.
|
||||
self.assertEqual(2, self.vol_drv.stg_ftsk.feed.reverse.call_count)
|
||||
|
||||
@mock.patch('pypowervm.tasks.vfc_mapper.'
|
||||
'build_migration_mappings')
|
||||
@mock.patch('nova_powervm.virt.powervm.volume.npiv.NPIVVolumeAdapter.'
|
||||
'_fabric_names')
|
||||
def test_pre_live_migration_on_destination(
|
||||
self, mock_fabric_names, mock_build_mig_map):
|
||||
mock_fabric_names.return_value = ['A', 'B']
|
||||
|
||||
mig_data = {'src_npiv_fabric_slots_A': jsonutils.dumps([1, 2]),
|
||||
'src_npiv_fabric_slots_B': jsonutils.dumps([3]),
|
||||
'src_vios_peer_slots': jsonutils.dumps([[1, 2, 3]])}
|
||||
|
||||
mock_build_mig_map.return_value = {'a', 'b'}
|
||||
self.vol_drv.stg_ftsk = mock.MagicMock()
|
||||
|
||||
# Execute the test
|
||||
self.vol_drv.pre_live_migration_on_destination(mig_data)
|
||||
|
||||
# Order of the mappings is not important.
|
||||
self.assertEqual(
|
||||
set({'b', 'a'}),
|
||||
set(jsonutils.loads(mig_data.get('vfc_lpm_mappings'))))
|
||||
|
||||
def test_set_fabric_meta(self):
|
||||
port_map = [('1', 'aa AA'), ('2', 'bb BB'),
|
||||
('3', 'cc CC'), ('4', 'dd DD'),
|
||||
|
||||
@@ -122,7 +122,9 @@ class NPIVVolumeAdapter(v_driver.FibreChannelVolumeAdapter):
|
||||
"""
|
||||
fabrics = self._fabric_names()
|
||||
vios_wraps = self.stg_ftsk.feed
|
||||
|
||||
# This mapping contains the client slots used on a given vios.
|
||||
# { vios_uuid: [slot_num, ...], vios2_uuid: [slot_num2,..] }
|
||||
slot_peer_dict = dict()
|
||||
for fabric in fabrics:
|
||||
npiv_port_maps = self._get_fabric_meta(fabric)
|
||||
if not npiv_port_maps:
|
||||
@@ -130,14 +132,24 @@ class NPIVVolumeAdapter(v_driver.FibreChannelVolumeAdapter):
|
||||
|
||||
client_slots = []
|
||||
for port_map in npiv_port_maps:
|
||||
vfc_map = pvm_vfcm.find_vios_for_vfc_wwpns(
|
||||
vios_wraps, port_map[1].split())[1]
|
||||
client_slots.append(vfc_map.client_adapter.lpar_slot_num)
|
||||
vios_w, vfc_map = pvm_vfcm.find_vios_for_vfc_wwpns(
|
||||
vios_wraps, port_map[1].split())
|
||||
slot_num = vfc_map.client_adapter.lpar_slot_num
|
||||
vios_uuid = vios_w.partition_uuid
|
||||
if vios_uuid not in slot_peer_dict:
|
||||
slot_peer_dict[vios_uuid] = []
|
||||
slot_peer_dict[vios_uuid].append(slot_num)
|
||||
client_slots.append(slot_num)
|
||||
|
||||
# Set the client slots into the fabric data to pass to the
|
||||
# destination. Only strings can be stored.
|
||||
mig_data['src_npiv_fabric_slots_%s' % fabric] = (
|
||||
jsonutils.dumps(client_slots))
|
||||
# The target really doesn't care what the UUID is of the source VIOS
|
||||
# it is on a different server. So let's strip that out and just
|
||||
# get the values.
|
||||
mig_data['src_vios_peer_slots'] = (
|
||||
jsonutils.dumps(list(slot_peer_dict.values())))
|
||||
|
||||
def pre_live_migration_on_destination(self, mig_data):
|
||||
"""Perform pre live migration steps for the volume on the target host.
|
||||
@@ -158,7 +170,34 @@ class NPIVVolumeAdapter(v_driver.FibreChannelVolumeAdapter):
|
||||
should be added to this dictionary.
|
||||
"""
|
||||
vios_wraps = self.stg_ftsk.feed
|
||||
if 'src_vios_peer_slots' in mig_data:
|
||||
self._pre_live_migration_on_dest_new(vios_wraps, mig_data)
|
||||
else:
|
||||
self._pre_live_migration_on_dest_legacy(vios_wraps, mig_data)
|
||||
|
||||
def _pre_live_migration_on_dest_new(self, vios_wraps, mig_data):
|
||||
# Need to first derive the port mappings that can be passed back
|
||||
# to the source system for the live migration call. This tells
|
||||
# the source system what 'vfc mappings' to pass in on the live
|
||||
# migration command.
|
||||
fabric_data = dict()
|
||||
for fabric in self._fabric_names():
|
||||
fab_slots = jsonutils.loads(
|
||||
mig_data['src_npiv_fabric_slots_%s' % fabric])
|
||||
ports = self._fabric_ports(fabric)
|
||||
fabric_data[fabric] = {'slots': fab_slots,
|
||||
'p_port_wwpns': ports}
|
||||
|
||||
slot_peers = jsonutils.loads(
|
||||
mig_data['src_vios_peer_slots'])
|
||||
fabric_mapping = pvm_vfcm.build_migration_mappings(
|
||||
vios_wraps, fabric_data, slot_peers)
|
||||
mig_data['vfc_lpm_mappings'] = jsonutils.dumps(fabric_mapping)
|
||||
|
||||
def _pre_live_migration_on_dest_legacy(self, vios_wraps, mig_data):
|
||||
# Used in case the source server is running an old nova-compute (ex.
|
||||
# Mitaka). To be removed in Ocata or Pike.
|
||||
#
|
||||
# Need to first derive the port mappings that can be passed back
|
||||
# to the source system for the live migration call. This tells
|
||||
# the source system what 'vfc mappings' to pass in on the live
|
||||
|
||||
Reference in New Issue
Block a user