Example #1
0
    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})
Example #2
0
    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
Example #3
0
    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())))
Example #4
0
    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
Example #5
0
    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))
Example #6
0
    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
Example #7
0
    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})
Example #8
0
    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)
Example #9
0
    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)
Example #10
0
    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
Example #11
0
    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)
Example #12
0
    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