Files
distcloud/distributedcloud/dcmanager/orchestrator/states/base.py
Gustavo Herzmann bd7d2a6922 Refactor distributed cloud patch orchestration
Makes PatchOrchThread a subclass of the OrchThread, following the same
design used in the other types of orchestration. It also removes the
strategy step to orchestrate the system controller patching, as it
should be done separately, before initializing the patch orchestration
to patch the subclouds.

The alarm checks that were executed inside the 'updating' state are now
part of a new 'pre-check' state that runs at the beggining of the
orchestration process.

It also refactors the unit tests to be in line with the unit tests of
the other orchestrator types by using the TestSwUpdate base class.

Test Plan:
1. PASS - Verify successfull patch orchestration by applying and
          removing a NRR patch;
2. PASS - Verify successfull patch orchestration by applying and
          removing a RR patch;
3. PASS - Induce a management affecting alarm in a subcloud and verify
          that orchestration fails for that subcloud;
4. PASS - Induce a 900.001 alarm by partially apply a patch in a
          subcloud beforehand and verify that orchestration completes
          successfully for that subcloud;
5. PASS - Create a DC orch patch strategy, then manually patch a
          subcloud using the sw-manager patch-strategy command and
          then apply the DC orch patch strategy, verifying if the
          state machine skips from the 'creating VIM patch strategy'
          state directly to the 'finishing patch strategy' state;
6. PASS - Verify that no strategy step is created for the system
          controller;
7. PASS - Execute another orchestration type (e.g. prestage) and
          verify that it still works as expected.

Story: 2010584
Task: 47371

Co-Authored-By: Al Bailey <al.bailey@windriver.com>
Signed-off-by: Gustavo Herzmann <gustavo.herzmann@windriver.com>
Change-Id: I2c37bd59696e6f9e4fd706f3b3c97f8f9e4499b0
2023-02-27 09:48:14 -03:00

168 lines
6.0 KiB
Python

#
# Copyright (c) 2020-2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import abc
import six
from oslo_log import log as logging
from dccommon import consts as dccommon_consts
from dccommon.drivers.openstack.barbican import BarbicanClient
from dccommon.drivers.openstack.fm import FmClient
from dccommon.drivers.openstack.patching_v1 import PatchingClient
from dccommon.drivers.openstack.sdk_platform import OpenStackDriver
from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
from dccommon.drivers.openstack.vim import VimClient
from dcmanager.common import context
from dcmanager.common.exceptions import InvalidParameterValue
LOG = logging.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class BaseState(object):
def __init__(self, next_state, region_name):
super(BaseState, self).__init__()
self.next_state = next_state
self.context = context.get_admin_context()
self._stop = None
self.region_name = region_name
self._shared_caches = None
self._job_data = None
def override_next_state(self, next_state):
self.next_state = next_state
def set_job_data(self, job_data):
"""Store an orch_thread job data object"""
self._job_data = job_data
def registerStopEvent(self, stop_event):
"""Store an orch_thread threading.Event to detect stop."""
self._stop = stop_event
def stopped(self):
"""Check if the threading.Event is set, otherwise return False."""
if self._stop is not None:
return self._stop.isSet()
else:
return False
def debug_log(self, strategy_step, details):
LOG.debug("Stage: %s, State: %s, Subcloud: %s, Details: %s"
% (strategy_step.stage,
strategy_step.state,
self.get_region_name(strategy_step),
details))
def info_log(self, strategy_step, details):
LOG.info("Stage: %s, State: %s, Subcloud: %s, Details: %s"
% (strategy_step.stage,
strategy_step.state,
self.get_region_name(strategy_step),
details))
def warn_log(self, strategy_step, details):
LOG.warn("Stage: %s, State: %s, Subcloud: %s, Details: %s"
% (strategy_step.stage,
strategy_step.state,
self.get_region_name(strategy_step),
details))
def error_log(self, strategy_step, details):
LOG.error("Stage: %s, State: %s, Subcloud: %s, Details: %s"
% (strategy_step.stage,
strategy_step.state,
self.get_region_name(strategy_step),
details))
def exception_log(self, strategy_step, details):
LOG.exception("Stage: %s, State: %s, Subcloud: %s, Details: %s"
% (strategy_step.stage,
strategy_step.state,
self.get_region_name(strategy_step),
details))
@staticmethod
def get_region_name(strategy_step):
"""Get the region name for a strategy step"""
if strategy_step.subcloud_id is None:
# This is the SystemController.
return dccommon_consts.DEFAULT_REGION_NAME
return strategy_step.subcloud.name
@staticmethod
def get_keystone_client(region_name=dccommon_consts.DEFAULT_REGION_NAME):
"""Construct a (cached) keystone client (and token)"""
try:
os_client = OpenStackDriver(region_name=region_name,
region_clients=['sysinv'])
return os_client.keystone_client
except Exception:
LOG.warning('Failure initializing KeystoneClient for region: %s'
% region_name)
raise
def get_sysinv_client(self, region_name):
"""construct a sysinv client
"""
keystone_client = self.get_keystone_client(region_name)
endpoint = keystone_client.endpoint_cache.get_endpoint('sysinv')
return SysinvClient(region_name, keystone_client.session,
endpoint=endpoint)
def get_fm_client(self, region_name):
keystone_client = self.get_keystone_client(region_name)
return FmClient(region_name, keystone_client.session)
def get_patching_client(self, region_name=dccommon_consts.DEFAULT_REGION_NAME):
keystone_client = self.get_keystone_client(region_name)
return PatchingClient(region_name, keystone_client.session)
@property
def local_sysinv(self):
return self.get_sysinv_client(dccommon_consts.DEFAULT_REGION_NAME)
@property
def subcloud_sysinv(self):
return self.get_sysinv_client(self.region_name)
def get_barbican_client(self, region_name):
"""construct a barbican client
"""
keystone_client = self.get_keystone_client(region_name)
return BarbicanClient(region_name, keystone_client.session)
def get_vim_client(self, region_name):
"""construct a vim client for a region."""
keystone_client = self.get_keystone_client(region_name)
return VimClient(region_name,
keystone_client.session)
def add_shared_caches(self, shared_caches):
# Shared caches not required by all states, so instantiate only if necessary
self._shared_caches = shared_caches
def _read_from_cache(self, cache_type, **filter_params):
if self._shared_caches is not None:
return self._shared_caches.read(cache_type, **filter_params)
else:
InvalidParameterValue(err="Specified cache type '%s' not "
"present" % cache_type)
@abc.abstractmethod
def perform_state_action(self, strategy_step):
"""Perform the action for this state on the strategy_step
Returns the next state in the state machine on success.
Any exceptions raised by this method set the strategy to FAILED.
"""
pass