190 lines
6.8 KiB
Python
190 lines
6.8 KiB
Python
# Copyright 2016, 2017 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 eventlet
|
|
from nova import utils as n_utils
|
|
from oslo_concurrency import lockutils
|
|
from oslo_log import log as logging
|
|
from pypowervm import const as pvm_const
|
|
from pypowervm import exceptions as pvm_exc
|
|
import six
|
|
import time
|
|
|
|
from nova_powervm.virt.powervm.nvram import api
|
|
from nova_powervm.virt.powervm import vm
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
LOCK_NVRAM_UPDT_LIST = 'nvram_update_list'
|
|
LOCK_NVRAM_STORE = 'nvram_update'
|
|
|
|
|
|
class NvramManager(object):
|
|
"""The manager of the NVRAM store and fetch process.
|
|
|
|
This class uses two locks. One for controlling access to the list of
|
|
instances to update the NVRAM for and another to control actually updating
|
|
the NVRAM for the instance itself.
|
|
|
|
An update to the instance store should always lock the update lock first
|
|
and then get the list lock. There should never be a case where the list
|
|
lock is acquired before the update lock. This can lead to deadlock cases.
|
|
|
|
NVRAM events for an instance come in spurts primarily during power on and
|
|
off, from what has been observed so far. By using a dictionary and the
|
|
instance.uuid as the key, rapid requests to store the NVRAM can be
|
|
collapsed down into a single request (optimal).
|
|
"""
|
|
|
|
def __init__(self, store_api, adapter, host_uuid):
|
|
"""Create the manager.
|
|
|
|
:param store_api: the NvramStore api to use.
|
|
:param adapter: pypowervm Adapter
|
|
:param host_uuid: powervm host uuid string
|
|
"""
|
|
super(NvramManager, self).__init__()
|
|
self._api = store_api
|
|
self._adapter = adapter
|
|
self._host_uuid = host_uuid
|
|
|
|
self._update_list = {}
|
|
self._queue = eventlet.queue.LightQueue()
|
|
self._shutdown = False
|
|
self._update_thread = n_utils.spawn(self._update_thread)
|
|
LOG.debug('NVRAM store manager started.')
|
|
|
|
def shutdown(self):
|
|
"""Shutdown the NVRAM Manager."""
|
|
|
|
LOG.debug('NVRAM store manager shutting down.')
|
|
self._shutdown = True
|
|
# Remove all pending updates
|
|
self._clear_list()
|
|
# Signal the thread to stop
|
|
self._queue.put(None)
|
|
self._update_thread.wait()
|
|
|
|
def store(self, instance, immediate=False):
|
|
"""Store the NVRAM for an instance.
|
|
|
|
:param instance: The instance to store the NVRAM for.
|
|
:param immediate: Force the update to take place immediately.
|
|
Otherwise, the request is queued for asynchronous
|
|
update.
|
|
"""
|
|
if immediate:
|
|
self._update_nvram(instance=instance)
|
|
else:
|
|
# Add it to the list to update
|
|
self._add_to_list(instance)
|
|
# Trigger the thread
|
|
self._queue.put(instance.uuid, block=False)
|
|
# Sleep so the thread gets a chance to run
|
|
time.sleep(0)
|
|
|
|
def fetch(self, instance):
|
|
"""Fetch the NVRAM for an instance.
|
|
|
|
:param instance: The instance to fetch the NVRAM for.
|
|
:returns: The NVRAM data for the instance.
|
|
"""
|
|
try:
|
|
return self._api.fetch(instance)
|
|
except Exception as e:
|
|
LOG.exception('Could not update NVRAM.', instance=instance)
|
|
raise api.NVRAMDownloadException(instance=instance.name,
|
|
reason=six.text_type(e))
|
|
|
|
@lockutils.synchronized(LOCK_NVRAM_STORE)
|
|
def remove(self, instance):
|
|
"""Remove the stored NVRAM for an instance.
|
|
|
|
:param instance: The instance for which the NVRAM will be removed.
|
|
"""
|
|
# Remove any pending updates
|
|
self._pop_from_list(uuid=instance.uuid)
|
|
# Remove it from the store
|
|
try:
|
|
self._api.delete(instance)
|
|
except Exception:
|
|
# Delete exceptions should not end the operation
|
|
LOG.exception('Could not delete NVRAM.', instance=instance)
|
|
|
|
@lockutils.synchronized(LOCK_NVRAM_UPDT_LIST)
|
|
def _add_to_list(self, instance):
|
|
"""Add an instance to the list of instances to store the NVRAM."""
|
|
self._update_list[instance.uuid] = instance
|
|
|
|
@lockutils.synchronized(LOCK_NVRAM_UPDT_LIST)
|
|
def _pop_from_list(self, uuid=None):
|
|
"""Pop an instance off the list of instance to update.
|
|
|
|
:param uuid: The uuid of the instance to update or if not specified
|
|
pull the next instance off the list.
|
|
returns: The uuid and instance.
|
|
"""
|
|
try:
|
|
if uuid is None:
|
|
return self._update_list.popitem()
|
|
else:
|
|
return self._update_list.pop(uuid)
|
|
except KeyError:
|
|
return None, None
|
|
|
|
@lockutils.synchronized(LOCK_NVRAM_UPDT_LIST)
|
|
def _clear_list(self):
|
|
"""Clear the list of instance to store NVRAM for."""
|
|
self._update_list.clear()
|
|
|
|
@lockutils.synchronized(LOCK_NVRAM_STORE)
|
|
def _update_nvram(self, instance=None):
|
|
"""Perform an update of NVRAM for instance.
|
|
|
|
:param instance: The instance to update or if not specified pull the
|
|
next one off the list to update.
|
|
"""
|
|
if instance is None:
|
|
uuid, instance = self._pop_from_list()
|
|
if uuid is None:
|
|
return
|
|
else:
|
|
# Remove any pending updates
|
|
self._pop_from_list(uuid=instance.uuid)
|
|
|
|
try:
|
|
LOG.debug('Updating NVRAM.', instance=instance)
|
|
data = vm.get_instance_wrapper(
|
|
self._adapter, instance, xag=[pvm_const.XAG.NVRAM]).nvram
|
|
LOG.debug('NVRAM for instance: %s', data, instance=instance)
|
|
if data is not None:
|
|
self._api.store(instance, data)
|
|
except pvm_exc.Error:
|
|
# Update exceptions should not end the operation.
|
|
LOG.exception('Could not update NVRAM.', instance=instance)
|
|
|
|
def _update_thread(self):
|
|
"""The thread that is charged with updating the NVRAM store."""
|
|
|
|
LOG.debug('NVRAM store manager update thread started.')
|
|
# Loop until it's time to shut down
|
|
while not self._shutdown:
|
|
if self._queue.get(block=True) is None:
|
|
LOG.debug('NVRAM store manager update thread is ending.')
|
|
return
|
|
|
|
self._update_nvram()
|
|
time.sleep(0)
|