def _remove_maps_for_fabric(self, fabric): """Removes the vFC storage mappings from the VM for a given fabric. :param fabric: The fabric to remove the mappings from. """ npiv_port_maps = self._get_fabric_meta(fabric) if not npiv_port_maps: # If no mappings exist, exit out of the method. return vios_wraps = self.stg_ftsk.feed for npiv_port_map in npiv_port_maps: ls = [LOG.info, _LI("Removing a NPIV mapping for instance " "%(inst)s for fabric %(fabric)s."), {'inst': self.instance.name, 'fabric': fabric}] vios_w = pvm_vfcm.find_vios_for_vfc_wwpns(vios_wraps, npiv_port_map[1])[0] if vios_w is not None: # Add the subtask to remove the specific map task_wrapper = self.stg_ftsk.wrapper_tasks[vios_w.uuid] task_wrapper.add_functor_subtask( pvm_vfcm.remove_maps, self.vm_uuid, port_map=npiv_port_map, logspec=ls) else: LOG.warn(_LW("No storage connections found between the " "Virtual I/O Servers and FC Fabric %(fabric)s."), {'fabric': fabric})
def post_live_migration_at_destination(self, mig_vol_stor): """Perform post live migration steps for the volume on the target host. This method performs any post live migration that is needed. Is not required to be implemented. :param mig_vol_stor: An unbounded dictionary that will be passed to each volume adapter during the post live migration call. Adapters can store data in here that may be used by subsequent volume adapters. """ vios_wraps = self.stg_ftsk.feed # This method will run on the target host after the migration is # completed. Right after this the instance.save is invoked from the # manager. Given that, we need to update the order of the WWPNs. # The first WWPN is the one that is logged into the fabric and this # will now indicate that our WWPN is logged in. LOG.debug('Post live migrate volume store: %s', mig_vol_stor, instance=self.instance) for fabric in self._fabric_names(): # We check the mig_vol_stor to see if this fabric has already been # flipped. If so, we can continue. fabric_key = '%s_flipped' % fabric if mig_vol_stor.get(fabric_key, False): continue # Must not be flipped, so execute the flip npiv_port_maps = self._get_fabric_meta(fabric) new_port_maps = [] for port_map in npiv_port_maps: # Flip the WPWNs c_wwpns = port_map[1].split() c_wwpns.reverse() LOG.debug('Flipping WWPNs, ports: %s wwpns: %s', port_map, c_wwpns, instance=self.instance) # Get the new physical WWPN. vfc_map = pvm_vfcm.find_vios_for_vfc_wwpns( vios_wraps, c_wwpns)[1] p_wwpn = vfc_map.backing_port.wwpn # Build the new map. new_map = (p_wwpn, " ".join(c_wwpns)) new_port_maps.append(new_map) self._set_fabric_meta(fabric, new_port_maps) self._set_fabric_state(fabric, FS_INST_MAPPED) # Store that this fabric is now flipped. mig_vol_stor[fabric_key] = True
def pre_live_migration_on_source(self, mig_data): """Performs pre live migration steps for the volume on the source host. Certain volume connectors may need to pass data from the source host to the target. This may be required to determine how volumes connect through the Virtual I/O Servers. This method gives the volume connector an opportunity to update the mig_data (a dictionary) with any data that is needed for the target host during the pre-live migration step. Since the source host has no native pre_live_migration step, this is invoked from check_can_live_migrate_source in the overall live migration flow. :param mig_data: A dictionary that the method can update to include data needed by the pre_live_migration_at_destination method. """ 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: continue client_slots = [] for port_map in npiv_port_maps: 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 post_live_migration_at_destination(self, mig_vol_stor): """Perform post live migration steps for the volume on the target host. This method performs any post live migration that is needed. Is not required to be implemented. :param mig_vol_stor: An unbounded dictionary that will be passed to each volume adapter during the post live migration call. Adapters can store data in here that may be used by subsequent volume adapters. """ vios_wraps = self.stg_ftsk.feed # This method will run on the target host after the migration is # completed. Right after this the instance.save is invoked from the # manager. Given that, we need to update the order of the WWPNs. # The first WWPN is the one that is logged into the fabric and this # will now indicate that our WWPN is logged in. LOG.debug('Post live migrate volume store: %s' % mig_vol_stor, instance=self.instance) for fabric in self._fabric_names(): # We check the mig_vol_stor to see if this fabric has already been # flipped. If so, we can continue. fabric_key = '%s_flipped' % fabric if mig_vol_stor.get(fabric_key, False): continue # Must not be flipped, so execute the flip npiv_port_maps = self._get_fabric_meta(fabric) new_port_maps = [] for port_map in npiv_port_maps: # Flip the WPWNs c_wwpns = port_map[1].split() c_wwpns.reverse() LOG.debug('Flipping WWPNs, ports: %s wwpns: %s' % (port_map, c_wwpns), instance=self.instance) # Get the new physical WWPN. vfc_map = pvm_vfcm.find_vios_for_vfc_wwpns(vios_wraps, c_wwpns)[1] p_wwpn = vfc_map.backing_port.wwpn # Build the new map. new_map = (p_wwpn, " ".join(c_wwpns)) new_port_maps.append(new_map) self._set_fabric_meta(fabric, new_port_maps) self._set_fabric_state(fabric, FS_INST_MAPPED) # Store that this fabric is now flipped. mig_vol_stor[fabric_key] = True
def pre_live_migration_on_source(self, mig_data): """Performs pre live migration steps for the volume on the source host. Certain volume connectors may need to pass data from the source host to the target. This may be required to determine how volumes connect through the Virtual I/O Servers. This method gives the volume connector an opportunity to update the mig_data (a dictionary) with any data that is needed for the target host during the pre-live migration step. Since the source host has no native pre_live_migration step, this is invoked from check_can_live_migrate_source in the overall live migration flow. :param mig_data: A dictionary that the method can update to include data needed by the pre_live_migration_at_destination method. """ fabrics = self._fabric_names() vios_wraps = self.stg_ftsk.feed for fabric in fabrics: npiv_port_maps = self._get_fabric_meta(fabric) if not npiv_port_maps: continue 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) # 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))
def pre_live_migration_on_source(self, mig_data): """Performs pre live migration steps for the volume on the source host. Certain volume connectors may need to pass data from the source host to the target. This may be required to determine how volumes connect through the Virtual I/O Servers. This method gives the volume connector an opportunity to update the mig_data (a dictionary) with any data that is needed for the target host during the pre-live migration step. Since the source host has no native pre_live_migration step, this is invoked from check_can_live_migrate_source in the overall live migration flow. :param mig_data: A dictionary that the method can update to include data needed by the pre_live_migration_at_destination method. """ fabrics = self._fabric_names() vios_wraps = self.stg_ftsk.feed for fabric in fabrics: npiv_port_maps = self._get_fabric_meta(fabric) if not npiv_port_maps: continue client_slots = [] for port_map in npiv_port_maps: vios_w, vfc_map = pvm_vfcm.find_vios_for_vfc_wwpns( vios_wraps, port_map[1].split()) client_slots.append(vfc_map.client_adapter.slot_number) # Set the client slots into the fabric data to pass to the # destination. mig_data['npiv_fabric_slots_%s' % fabric] = client_slots
def _remove_maps_for_fabric(self, fabric): """Removes the vFC storage mappings from the VM for a given fabric. :param fabric: The fabric to remove the mappings from. """ npiv_port_maps = self._get_fabric_meta(fabric) if not npiv_port_maps: # If no mappings exist, exit out of the method. return vios_wraps = self.stg_ftsk.feed for npiv_port_map in npiv_port_maps: ls = [ LOG.info, _LI("Removing a NPIV mapping for instance " "%(inst)s for fabric %(fabric)s."), { 'inst': self.instance.name, 'fabric': fabric } ] vios_w = pvm_vfcm.find_vios_for_vfc_wwpns(vios_wraps, npiv_port_map[1])[0] if vios_w is not None: # Add the subtask to remove the specific map task_wrapper = self.stg_ftsk.wrapper_tasks[vios_w.uuid] task_wrapper.add_functor_subtask(pvm_vfcm.remove_maps, self.vm_uuid, port_map=npiv_port_map, logspec=ls) else: LOG.warn( _LW("No storage connections found between the " "Virtual I/O Servers and FC Fabric %(fabric)s."), {'fabric': fabric})
def test_find_vios_for_vfc_wwpns(self): """Tests the find_vios_for_vfc_wwpns method.""" # This WWPN is on the first VIOS v_wwpns = ['c05076079cff0e56', 'c05076079cff0e57'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[0], vios) self.assertEqual('10000090FA5371F2', vmap.backing_port.wwpn) # Have one of the ports be wrong v_wwpns = ['c05076079cff0e56', 'c05076079cff0e59'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertIsNone(vios) self.assertIsNone(vmap) # Try odd formatting v_wwpns = ['C05076079cff0E56', 'c0:50:76:07:9c:ff:0E:57'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[0], vios) self.assertEqual('10000090FA5371F2', vmap.backing_port.wwpn) # Second VIOS v_wwpns = ['c05076079cff07ba', 'c05076079cff07bb'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[1], vios) self.assertEqual('10000090FA53720A', vmap.backing_port.wwpn) # Reverse WWPNs v_wwpns = ['c05076079cff07bb', 'c05076079cff07ba'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[1], vios) self.assertEqual('10000090FA53720A', vmap.backing_port.wwpn) # Set Type v_wwpns = {'c05076079cff07bb', 'c05076079cff07ba'} vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[1], vios) self.assertEqual('10000090FA53720A', vmap.backing_port.wwpn)
def pre_live_migration_on_destination(self, src_mig_data, dest_mig_data): """Perform pre live migration steps for the volume on the target host. This method performs any pre live migration that is needed. Certain volume connectors may need to pass data from the source host to the target. This may be required to determine how volumes connect through the Virtual I/O Servers. 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 src_mig_data: The migration data from the source server. :param dest_mig_data: The 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. """ vios_wraps = self.stg_ftsk.feed mgmt_uuid = mgmt.get_mgmt_partition(self.adapter).uuid # Each mapping should attempt to remove itself from the management # partition. for fabric in self._fabric_names(): npiv_port_maps = self._get_fabric_meta(fabric) # 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. slots = src_mig_data['npiv_fabric_slots_%s' % fabric] fabric_mapping = pvm_vfcm.build_migration_mappings_for_fabric( vios_wraps, self._fabric_ports(fabric), slots) dest_mig_data['npiv_fabric_mapping_%s' % fabric] = fabric_mapping # Next we need to remove the mappings off the mgmt partition. for npiv_port_map in npiv_port_maps: ls = [ LOG.info, _LI("Removing mgmt NPIV mapping for instance " "%(inst)s for fabric %(fabric)s."), { 'inst': self.instance.name, 'fabric': fabric } ] vios_w, vfc_map = pvm_vfcm.find_vios_for_vfc_wwpns( vios_wraps, npiv_port_map[1].split()) if vios_w is not None: # Add the subtask to remove the mapping from the management # partition. task_wrapper = self.stg_ftsk.wrapper_tasks[vios_w.uuid] task_wrapper.add_functor_subtask( pvm_vfcm.remove_maps, mgmt_uuid, client_adpt=vfc_map.client_adapter, logspec=ls) else: LOG.warn( _LW("No storage connections found between the " "Virtual I/O Servers and FC Fabric " "%(fabric)s. The connection might be removed " "already."), {'fabric': fabric}) # TODO(thorst) Find a better place for this execute. Works for now # as the stg_ftsk is all local. Also won't do anything if there # happen to be no fabric changes. self.stg_ftsk.execute() # Collate all of the individual fabric mappings into a single element. full_map = [] for key, value in dest_mig_data.items(): if key.startswith('npiv_fabric_mapping_'): full_map.extend(value) dest_mig_data['vfc_lpm_mappings'] = full_map
def test_find_vios_for_vfc_wwpns(self): """Tests the find_vios_for_vfc_wwpns method.""" # This WWPN is on the first VIOS v_wwpns = ['c05076079cff0e56', 'c05076079cff0e57'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[0], vios) self.assertEqual('10000090FA5371F2', vmap.backing_port.wwpn) # Have one of the ports be wrong v_wwpns = ['c05076079cff0e56', 'c05076079cff0e59'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertIsNone(vios) self.assertIsNone(vmap) # Try odd formatting v_wwpns = ['C05076079cff0E56', 'c0:50:76:07:9c:ff:0E:57'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[0], vios) self.assertEqual('10000090FA5371F2', vmap.backing_port.wwpn) # Second VIOS v_wwpns = ['c05076079cff07ba', 'c05076079cff07bb'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[1], vios) self.assertEqual('10000090FA53720A', vmap.backing_port.wwpn) # Reverse WWPNs v_wwpns = ['c05076079cff07bb', 'c05076079cff07ba'] vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[1], vios) self.assertEqual('10000090FA53720A', vmap.backing_port.wwpn) # Set Type v_wwpns = {'c05076079cff07bb', 'c05076079cff07ba'} vios, vmap = vfc_mapper.find_vios_for_vfc_wwpns(self.entries, v_wwpns) self.assertEqual(self.entries[1], vios) self.assertEqual('10000090FA53720A', vmap.backing_port.wwpn) # test to check fabrics with no backing port are ignored mock_client_adap1 = mock.create_autospec(pvm_stor.VFCClientAdapter, spec_set=True) mock_client_adap1.configure_mock( wwpns=['C05076065A7C02E4', 'C05076065A7C02E5']) mock_map1 = mock.create_autospec(pvm_vios.VFCMapping, spec_set=True) mock_map1.configure_mock(backing_port=None, client_adapter=mock_client_adap1) vios_w = mock.Mock(vfc_mappings=[mock_map1]) v_port_wwpns = ['C05076065A7C02E4', 'C05076065A7C02E5'] vmap = vfc_mapper.find_vios_for_vfc_wwpns([vios_w], v_port_wwpns)[1] self.assertIsNone(vmap) mock_client_adap2 = mock.create_autospec(pvm_stor.VFCClientAdapter, spec_set=True) mock_client_adap2.configure_mock( wwpns=['C05076065A7C02E4', 'C05076065A7C02E5']) mock_map2 = mock.create_autospec(pvm_vios.VFCMapping, spec_set=True) mock_map2.configure_mock(backing_port="port1", client_adapter=mock_client_adap2) vios_w = mock.Mock(vfc_mappings=[mock_map1, mock_map2]) v_port_wwpns = ['C05076065A7C02E4', 'C05076065A7C02E5'] vmap = vfc_mapper.find_vios_for_vfc_wwpns([vios_w], v_port_wwpns)[1] self.assertEqual(mock_map2, vmap)
def pre_live_migration_on_destination(self, src_mig_data, dest_mig_data): """Perform pre live migration steps for the volume on the target host. This method performs any pre live migration that is needed. Certain volume connectors may need to pass data from the source host to the target. This may be required to determine how volumes connect through the Virtual I/O Servers. 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 src_mig_data: The migration data from the source server. :param dest_mig_data: The 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. """ vios_wraps = self.stg_ftsk.feed mgmt_uuid = mgmt.get_mgmt_partition(self.adapter).uuid # Each mapping should attempt to remove itself from the management # partition. for fabric in self._fabric_names(): npiv_port_maps = self._get_fabric_meta(fabric) # 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. slots = src_mig_data['npiv_fabric_slots_%s' % fabric] fabric_mapping = pvm_vfcm.build_migration_mappings_for_fabric( vios_wraps, self._fabric_ports(fabric), slots) dest_mig_data['npiv_fabric_mapping_%s' % fabric] = fabric_mapping # Next we need to remove the mappings off the mgmt partition. for npiv_port_map in npiv_port_maps: ls = [LOG.info, _LI("Removing mgmt NPIV mapping for instance " "%(inst)s for fabric %(fabric)s."), {'inst': self.instance.name, 'fabric': fabric}] vios_w, vfc_map = pvm_vfcm.find_vios_for_vfc_wwpns( vios_wraps, npiv_port_map[1].split()) if vios_w is not None: # Add the subtask to remove the mapping from the management # partition. task_wrapper = self.stg_ftsk.wrapper_tasks[vios_w.uuid] task_wrapper.add_functor_subtask( pvm_vfcm.remove_maps, mgmt_uuid, client_adpt=vfc_map.client_adapter, logspec=ls) else: LOG.warn(_LW("No storage connections found between the " "Virtual I/O Servers and FC Fabric " "%(fabric)s. The connection might be removed " "already."), {'fabric': fabric}) # TODO(thorst) Find a better place for this execute. Works for now # as the stg_ftsk is all local. Also won't do anything if there # happen to be no fabric changes. self.stg_ftsk.execute() # Collate all of the individual fabric mappings into a single element. full_map = [] for key, value in dest_mig_data.items(): if key.startswith('npiv_fabric_mapping_'): full_map.extend(value) dest_mig_data['vfc_lpm_mappings'] = full_map