189 lines
7.3 KiB
Python
189 lines
7.3 KiB
Python
# Copyright 2016 IBM Corp.
|
|
#
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import six
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from pypowervm import exceptions as pvm_exc
|
|
from pypowervm.tasks import slot_map
|
|
from pypowervm.tasks import storage as pvm_tstor
|
|
|
|
from nova_powervm.virt.powervm import exception as p_exc
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
_SLOT_KEY = "CLIENT_SLOT_DATA"
|
|
|
|
|
|
def build_slot_mgr(instance, store_api, adapter=None, vol_drv_iter=None):
|
|
"""Builds the NovaSlotManager
|
|
|
|
A slot manager serves two purposes. First is to store the 'slots' in which
|
|
client adapters are created. These adapters host I/O to the VMs and
|
|
it is important to save which VM slots these adapters are created within.
|
|
|
|
The second purpose for the slot mgr is to consume that data. When a VM
|
|
rebuild operation is kicked off, the client adapters must go into the
|
|
same exact slots. This slot mgr serves up that necessary metadata.
|
|
|
|
:param instance: The nova instance to get the slot map for.
|
|
:param store_api: The Swift Storage API that will save the Slot Map data.
|
|
This may be None, indicating that the Slot Map data
|
|
should not be persisted.
|
|
:param adapter: pypowervm.adapter.Adapter for REST communication. Required
|
|
if rebuilding. May be omitted if the slot map instance
|
|
will only be used for saving source mappings.
|
|
:param vol_drv_iter: Iterator over volume drivers in this driver instance.
|
|
May be omitted if the slot map instance will only be
|
|
used for saving source mappings.
|
|
:return: The appropriate PowerVM SlotMapStore implementation. If the NVRAM
|
|
store is set up, a Swift-backed implementation is returned. If
|
|
there is no NVRAM set up, a no-op implementation is returned.
|
|
"""
|
|
if store_api is not None:
|
|
return SwiftSlotManager(store_api, instance=instance, adapter=adapter,
|
|
vol_drv_iter=vol_drv_iter)
|
|
return NoopSlotManager(instance=instance)
|
|
|
|
|
|
class NovaSlotManager(slot_map.SlotMapStore):
|
|
"""Used to manage the slots for a PowerVM-based system.
|
|
|
|
Slots are used by the Virtual Machine to place 'adapters' on the system.
|
|
This slot store serves two purposes. It will guide the spawn (or rebuild)
|
|
operation with what slots it should use.
|
|
|
|
Second, it serves as a storage of the client slot data (which can then
|
|
be saved to an external location for rebuild server scenarios).
|
|
|
|
This class extends the base pypowervm facilities for this, but adds the
|
|
context of the 'Nova' objects. It should be extended by the backing
|
|
storage implementations.
|
|
"""
|
|
|
|
def __init__(self, instance=None, adapter=None, vol_drv_iter=None):
|
|
super(NovaSlotManager, self).__init__(
|
|
'%s_slot_map' % instance.uuid)
|
|
self.instance = instance
|
|
self.adapter = adapter
|
|
self.vol_drv_iter = vol_drv_iter if vol_drv_iter else ()
|
|
self._build_map = None
|
|
self._vios_wraps = []
|
|
self.is_rebuild = (self.adapter and vol_drv_iter)
|
|
|
|
@property
|
|
def build_map(self):
|
|
"""Returns a 'BuildSlotMap' from pypowervm.
|
|
|
|
Identifies for build out of a VM what slots should be used for the
|
|
adapters.
|
|
"""
|
|
if self._build_map is None:
|
|
if self.is_rebuild:
|
|
self.init_recreate_map(self.adapter, self.vol_drv_iter)
|
|
else:
|
|
self._build_map = slot_map.BuildSlotMap(self)
|
|
return self._build_map
|
|
|
|
def init_recreate_map(self, adapter, vol_drv_iter):
|
|
"""To be used on a target system. Builds the 'slot recreate' map.
|
|
|
|
This is to initialize on the target system how the client slots should
|
|
be rebuilt on the client VM.
|
|
|
|
This should not be called unless it is a VM recreate.
|
|
|
|
:param adapter: The pypowervm adapter.
|
|
:param vol_drv_iter: An iterator of the volume drivers.
|
|
"""
|
|
# This should only be called on a rebuild. Focus on being correct
|
|
# first. Performance is secondary.
|
|
|
|
# We need to scrub existing stale mappings, including those for the VM
|
|
# we're creating. It is critical that this happen *before* we create
|
|
# any of the mappings we actually want this VM to have.
|
|
scrub_ftsk = pvm_tstor.ComprehensiveScrub(adapter)
|
|
scrub_ftsk.execute()
|
|
self._vios_wraps = scrub_ftsk.feed
|
|
|
|
pv_vscsi_vol_to_vio = {}
|
|
fabric_names = []
|
|
for bdm, vol_drv in vol_drv_iter:
|
|
if vol_drv.vol_type() in ['vscsi', 'fileio']:
|
|
self._pv_vscsi_vol_to_vio(vol_drv, pv_vscsi_vol_to_vio)
|
|
elif len(fabric_names) == 0 and vol_drv.vol_type() == 'npiv':
|
|
fabric_names = vol_drv._fabric_names()
|
|
|
|
# Run the full initialization now that we have the pre-requisite data
|
|
try:
|
|
self._build_map = slot_map.RebuildSlotMap(
|
|
self, self._vios_wraps, pv_vscsi_vol_to_vio, fabric_names)
|
|
except pvm_exc.InvalidHostForRebuild as e:
|
|
raise p_exc.InvalidRebuild(error=six.text_type(e))
|
|
|
|
def _pv_vscsi_vol_to_vio(self, vol_drv, vol_to_vio):
|
|
"""Find which physical volumes are on what VIOSes.
|
|
|
|
Builds: { "udid" : [ "vios_uuid", "vios_uuid"], ...}
|
|
"""
|
|
for vios_w in self._vios_wraps:
|
|
on_vio, udid = vol_drv.is_volume_on_vios(vios_w)
|
|
if not on_vio:
|
|
continue
|
|
|
|
if udid not in vol_to_vio:
|
|
vol_to_vio[udid] = []
|
|
vol_to_vio[udid].append(vios_w.uuid)
|
|
|
|
|
|
class SwiftSlotManager(NovaSlotManager):
|
|
"""Used to store the slot metadata for the VM.
|
|
|
|
When rebuilding a PowerVM virtual machine, the slots must line up in their
|
|
original location. This is so that the VM boots from the same location
|
|
and the boot order (as well as other data) is preserved.
|
|
|
|
This implementation is used to store the implementation in Swift. It is
|
|
only used if the operator has choosen to use Swift to store the NVRAM
|
|
metadata.
|
|
"""
|
|
|
|
def __init__(self, store_api, **kwargs):
|
|
self.store_api = store_api
|
|
super(SwiftSlotManager, self).__init__(**kwargs)
|
|
|
|
def _load(self, key):
|
|
return self.store_api.fetch_slot_map(key)
|
|
|
|
def _save(self, key, blob):
|
|
self.store_api.store_slot_map(key, blob)
|
|
|
|
def _delete(self, key):
|
|
try:
|
|
self.store_api.delete_slot_map(key)
|
|
except Exception:
|
|
LOG.warning("Unable to delete the slot map from Swift backing "
|
|
"store with ID %(key)s. Will require "
|
|
"manual cleanup.", {'key': key},
|
|
instance=self.instance)
|
|
|
|
|
|
class NoopSlotManager(NovaSlotManager):
|
|
"""No op Slot Map (for when Swift is not used - which is standard)."""
|
|
|
|
pass
|