Merge "Implementing Lock and Unlock upgrade strategy states"
This commit is contained in:
commit
3514f5cd9a
|
@ -80,11 +80,38 @@ class SysinvClient(base.DriverBase):
|
|||
except exceptions.ServiceUnavailable:
|
||||
raise
|
||||
|
||||
def get_host(self, hostname_or_id):
|
||||
"""Get a host by its hostname or id."""
|
||||
return self.sysinv_client.ihost.get(hostname_or_id)
|
||||
|
||||
def get_controller_hosts(self):
|
||||
"""Get a list of controller hosts."""
|
||||
return self.sysinv_client.ihost.list_personality(
|
||||
sysinv_constants.CONTROLLER)
|
||||
|
||||
def _do_host_action(self, host_id, action_value):
|
||||
"""Protected method to invoke an action on a host."""
|
||||
patch = [{'op': 'replace',
|
||||
'path': '/action',
|
||||
'value': action_value}, ]
|
||||
return self.sysinv_client.ihost.update(host_id, patch)
|
||||
|
||||
def lock_host(self, host_id, force=False):
|
||||
"""Lock a host"""
|
||||
if force:
|
||||
action_value = 'force-lock'
|
||||
else:
|
||||
action_value = 'lock'
|
||||
return self._do_host_action(host_id, action_value)
|
||||
|
||||
def unlock_host(self, host_id, force=False):
|
||||
"""Unlock a host"""
|
||||
if force:
|
||||
action_value = 'force-unlock'
|
||||
else:
|
||||
action_value = 'unlock'
|
||||
return self._do_host_action(host_id, action_value)
|
||||
|
||||
def get_management_interface(self, hostname):
|
||||
"""Get the management interface for a host."""
|
||||
interfaces = self.sysinv_client.iinterface.list(hostname)
|
||||
|
|
|
@ -39,6 +39,10 @@ MANAGEMENT_MANAGED = "managed"
|
|||
AVAILABILITY_OFFLINE = "offline"
|
||||
AVAILABILITY_ONLINE = "online"
|
||||
|
||||
# Admin status for hosts
|
||||
ADMIN_LOCKED = 'locked'
|
||||
ADMIN_UNLOCKED = 'unlocked'
|
||||
|
||||
# Subcloud sync status
|
||||
SYNC_STATUS_UNKNOWN = "unknown"
|
||||
SYNC_STATUS_IN_SYNC = "in-sync"
|
||||
|
@ -99,7 +103,7 @@ STRATEGY_STATE_FAILED = "failed"
|
|||
|
||||
STRATEGY_STATE_INSTALLING_LICENSE = "installing license"
|
||||
STRATEGY_STATE_IMPORTING_LOAD = "importing load"
|
||||
STRATEGY_STATE_STARTING = "starting"
|
||||
STRATEGY_STATE_STARTING_UPGRADE = "starting upgrade"
|
||||
STRATEGY_STATE_LOCKING_CONTROLLER = "locking controller"
|
||||
STRATEGY_STATE_UPGRADING_SIMPLEX = "upgrading simplex"
|
||||
STRATEGY_STATE_MIGRATING_DATA = "migrating data"
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import abc
|
||||
import six
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dccommon.drivers.openstack.sdk_platform import OpenStackDriver
|
||||
from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
|
||||
from dcmanager.common import consts
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseState(object):
|
||||
|
||||
def __init__(self):
|
||||
super(BaseState, self).__init__()
|
||||
|
||||
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))
|
||||
|
||||
@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 consts.DEFAULT_REGION_NAME
|
||||
return strategy_step.subcloud.name
|
||||
|
||||
@staticmethod
|
||||
def get_keystone_client(region_name=consts.DEFAULT_REGION_NAME):
|
||||
"""Construct a (cached) keystone client (and token)"""
|
||||
|
||||
try:
|
||||
os_client = OpenStackDriver(region_name=region_name,
|
||||
region_clients=None)
|
||||
return os_client.keystone_client
|
||||
except Exception:
|
||||
LOG.warning('Failure initializing KeystoneClient for region: %s'
|
||||
% region_name)
|
||||
raise
|
||||
|
||||
@staticmethod
|
||||
def get_sysinv_client(region_name, session):
|
||||
"""construct a sysinv client
|
||||
|
||||
todo(abailey): determine if this client can be cached
|
||||
"""
|
||||
return SysinvClient(region_name, session)
|
||||
|
||||
@abc.abstractmethod
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Perform the action for this state on the strategy_step"""
|
||||
pass
|
|
@ -0,0 +1,77 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dcmanager.common.consts import ADMIN_LOCKED
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
DEFAULT_MAX_QUERIES = 6
|
||||
DEFAULT_SLEEP_DURATION = 10
|
||||
|
||||
|
||||
class LockHostState(BaseState):
|
||||
"""Orchestration state for locking a host"""
|
||||
|
||||
def __init__(self,
|
||||
hostname='controller-0',
|
||||
max_queries=DEFAULT_MAX_QUERIES,
|
||||
sleep_duration=DEFAULT_SLEEP_DURATION):
|
||||
super(LockHostState, self).__init__()
|
||||
self.target_hostname = hostname
|
||||
# max time to wait (in seconds) is: sleep_duration * max_queries
|
||||
self.sleep_duration = sleep_duration
|
||||
self.max_queries = max_queries
|
||||
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Locks a host on the subcloud
|
||||
|
||||
Any exceptions raised by this method set the strategy to FAILED
|
||||
Returning normally from this method set the strategy to the next step
|
||||
"""
|
||||
|
||||
# Create a sysinv client on the subcloud
|
||||
ks_client = self.get_keystone_client(strategy_step.subcloud.name)
|
||||
sysinv_client = self.get_sysinv_client(strategy_step.subcloud.name,
|
||||
ks_client.session)
|
||||
|
||||
host = sysinv_client.get_host(self.target_hostname)
|
||||
|
||||
# if the host is already in the desired state, no need for action
|
||||
if host.administrative == ADMIN_LOCKED:
|
||||
msg = "Host: %s already: %s." % (self.target_hostname,
|
||||
host.administrative)
|
||||
self.debug_log(strategy_step, msg)
|
||||
return True
|
||||
|
||||
# Invoke the action
|
||||
# ihost_action is 'lock' and task is set to 'Locking'
|
||||
response = sysinv_client.lock_host(host.id)
|
||||
if (response.ihost_action != 'lock' or response.task != 'Locking'):
|
||||
raise Exception("Unable to lock host %s" % self.target_hostname)
|
||||
|
||||
# this action is asynchronous, query until it completes or times out
|
||||
counter = 0
|
||||
while True:
|
||||
# query the administrative state to see if it is the new state.
|
||||
host = sysinv_client.get_host(self.target_hostname)
|
||||
if host.administrative == ADMIN_LOCKED:
|
||||
msg = "Host: %s is now: %s" % (self.target_hostname,
|
||||
host.administrative)
|
||||
self.debug_log(strategy_step, msg)
|
||||
break
|
||||
counter += 1
|
||||
if counter >= self.max_queries:
|
||||
raise Exception("Timeout waiting for lock to complete")
|
||||
time.sleep(self.sleep_duration)
|
||||
# todo(abailey): add support for checking if the thread is stopped
|
||||
|
||||
# If we are here, the loop broke out cleanly and the action succeeded
|
||||
# When we return from this method without throwing an exception, the
|
||||
# state machine can proceed to the next state
|
||||
return True
|
|
@ -0,0 +1,80 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dcmanager.common.consts import ADMIN_UNLOCKED
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
DEFAULT_MAX_QUERIES = 6
|
||||
DEFAULT_SLEEP_DURATION = 10
|
||||
|
||||
|
||||
class UnlockHostState(BaseState):
|
||||
"""Orchestration state for unlocking a host"""
|
||||
|
||||
def __init__(self,
|
||||
hostname='controller-0',
|
||||
max_queries=DEFAULT_MAX_QUERIES,
|
||||
sleep_duration=DEFAULT_SLEEP_DURATION):
|
||||
super(UnlockHostState, self).__init__()
|
||||
self.target_hostname = hostname
|
||||
# max time to wait (in seconds) is: sleep_duration * max_queries
|
||||
self.sleep_duration = sleep_duration
|
||||
self.max_queries = max_queries
|
||||
|
||||
def check_async_counter(self, counter):
|
||||
if counter >= self.max_queries:
|
||||
raise Exception("Timeout waiting for unlock to complete")
|
||||
time.sleep(self.sleep_duration)
|
||||
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Unlocks a host on the subcloud
|
||||
|
||||
Any exceptions raised by this method set the strategy to FAILED
|
||||
Returning normally from this method set the strategy to the next step
|
||||
"""
|
||||
|
||||
# Create a sysinv client on the subcloud
|
||||
ks_client = self.get_keystone_client(strategy_step.subcloud.name)
|
||||
sysinv_client = self.get_sysinv_client(strategy_step.subcloud.name,
|
||||
ks_client.session)
|
||||
|
||||
host = sysinv_client.get_host(self.target_hostname)
|
||||
|
||||
# if the host is already in the desired state, no need for action
|
||||
if host.administrative == ADMIN_UNLOCKED:
|
||||
msg = "Host: %s already: %s." % (self.target_hostname,
|
||||
host.administrative)
|
||||
self.debug_log(strategy_step, msg)
|
||||
return True
|
||||
|
||||
# Invoke the action
|
||||
# ihost_action is 'unlock' and task is set to 'Unlocking'
|
||||
response = sysinv_client.unlock_host(host.id)
|
||||
if (response.ihost_action != 'unlock' or response.task != 'Unlocking'):
|
||||
raise Exception("Unable to unlock host %s" % self.target_hostname)
|
||||
|
||||
# this action is asynchronous, query until it completes or times out
|
||||
async_counter = 0
|
||||
while True:
|
||||
# query the administrative state to see if it is the new state.
|
||||
host = sysinv_client.get_host(self.target_hostname)
|
||||
if host.administrative == ADMIN_UNLOCKED:
|
||||
msg = "Host: %s is now: %s" % (self.target_hostname,
|
||||
host.administrative)
|
||||
self.debug_log(strategy_step, msg)
|
||||
break
|
||||
async_counter += 1
|
||||
# check_async_counter throws exception if loops exceeded or aborted
|
||||
self.check_async_counter(async_counter)
|
||||
|
||||
# If we are here, the loop broke out cleanly and the action succeeded
|
||||
# When we return from this method without throwing an exception, the
|
||||
# state machine can proceed to the next state
|
||||
return True
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ActivatingState(BaseState):
|
||||
"""Upgrade state actions for activating an upgrade"""
|
||||
|
||||
def __init__(self):
|
||||
super(ActivatingState, self).__init__()
|
||||
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Activate an upgrade on a subcloud
|
||||
|
||||
Any exceptions raised by this method set the strategy to FAILED
|
||||
Returning normally from this method set the strategy to the next step
|
||||
"""
|
||||
LOG.warning("ActivatingState has not been implemented yet.")
|
||||
|
||||
# When we return from this method without throwing an exception, the
|
||||
# state machine can proceed to the next state
|
||||
LOG.warning("Faking transition to next state")
|
||||
return True
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CompletingState(BaseState):
|
||||
"""Upgrade state actions for completing an upgrade"""
|
||||
|
||||
def __init__(self):
|
||||
super(CompletingState, self).__init__()
|
||||
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Complete an upgrade on a subcloud
|
||||
|
||||
Any exceptions raised by this method set the strategy to FAILED
|
||||
Returning normally from this method set the strategy to the next step
|
||||
"""
|
||||
LOG.warning("CompletingState has not been implemented yet.")
|
||||
|
||||
# When we return from this method without throwing an exception, the
|
||||
# state machine can proceed to the next state
|
||||
LOG.warning("Faking transition to next state")
|
||||
return True
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ImportLoadState(BaseState):
|
||||
"""Upgrade state for importing a load"""
|
||||
|
||||
def __init__(self):
|
||||
super(ImportLoadState, self).__init__()
|
||||
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Import a load on a subcloud
|
||||
|
||||
Any exceptions raised by this method set the strategy to FAILED
|
||||
Returning normally from this method set the strategy to the next step
|
||||
"""
|
||||
LOG.warning("ImportLoadState has not been implemented yet.")
|
||||
|
||||
# When we return from this method without throwing an exception, the
|
||||
# state machine can proceed to the next state
|
||||
LOG.warning("Faking transition to next state")
|
||||
return True
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MigratingDataState(BaseState):
|
||||
"""Upgrade step for migrating data"""
|
||||
|
||||
def __init__(self):
|
||||
super(MigratingDataState, self).__init__()
|
||||
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Migrate data for an upgrade on a subcloud
|
||||
|
||||
Any exceptions raised by this method set the strategy to FAILED
|
||||
Returning normally from this method set the strategy to the next step
|
||||
"""
|
||||
LOG.warning("MigratingDataState has not been implemented yet.")
|
||||
|
||||
# When we return from this method without throwing an exception, the
|
||||
# state machine can proceed to the next state
|
||||
LOG.warning("Faking transition to next state")
|
||||
return True
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StartingUpgradeState(BaseState):
|
||||
"""Upgrade state for starting an upgrade on a subcloud"""
|
||||
|
||||
def __init__(self):
|
||||
super(StartingUpgradeState, self).__init__()
|
||||
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Start an upgrade on a subcloud
|
||||
|
||||
Any exceptions raised by this method set the strategy to FAILED
|
||||
Returning normally from this method set the strategy to the next step
|
||||
"""
|
||||
LOG.warning("StartingUpgradeState has not been implemented yet.")
|
||||
|
||||
# When we return from this method without throwing an exception, the
|
||||
# state machine can proceed to the next state
|
||||
LOG.warning("Faking transition to next state")
|
||||
return True
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from oslo_log import log as logging
|
||||
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UpgradingSimplexState(BaseState):
|
||||
"""Upgrade state for upgrading a simplex subcloud host"""
|
||||
|
||||
def __init__(self):
|
||||
super(UpgradingSimplexState, self).__init__()
|
||||
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Upgrade a simplex host on a subcloud
|
||||
|
||||
Any exceptions raised by this method set the strategy to FAILED
|
||||
Returning normally from this method set the strategy to the next step
|
||||
"""
|
||||
LOG.warning("UpgradingSimplexState has not been implemented yet.")
|
||||
|
||||
# When we return from this method without throwing an exception, the
|
||||
# state machine can proceed to the next state
|
||||
LOG.warning("Faking transition to next state")
|
||||
return True
|
|
@ -196,7 +196,7 @@ class SwUpdateManager(manager.Manager):
|
|||
if strategy_type == consts.SW_UPDATE_TYPE_PATCH:
|
||||
db_api.strategy_step_create(
|
||||
context,
|
||||
None,
|
||||
None, # None means not a subcloud. ie: SystemController
|
||||
stage=1,
|
||||
state=consts.STRATEGY_STATE_INITIAL,
|
||||
details='')
|
||||
|
|
|
@ -111,6 +111,15 @@ class SwUpgradeOrchThread(threading.Thread):
|
|||
return consts.DEFAULT_REGION_NAME
|
||||
return strategy_step.subcloud.name
|
||||
|
||||
@staticmethod
|
||||
def format_update_details(last_state, info):
|
||||
# include the last state, since the current state is likely 'failed'
|
||||
details = "%s: %s" % (last_state, info)
|
||||
# details cannot exceed 255 chars. truncate and add '..'
|
||||
if len(details) > 255:
|
||||
details = details[:253] + '..'
|
||||
return details
|
||||
|
||||
@staticmethod
|
||||
def license_up_to_date(target_license, existing_license):
|
||||
return target_license == existing_license
|
||||
|
@ -289,6 +298,8 @@ class SwUpgradeOrchThread(threading.Thread):
|
|||
strategy_step,
|
||||
self.install_subcloud_license,
|
||||
log_error=True)
|
||||
# todo(abailey): state and their method invoked can be managed
|
||||
# using a dictionary to make this more maintainable.
|
||||
elif strategy_step.state == \
|
||||
consts.STRATEGY_STATE_INSTALLING_LICENSE:
|
||||
self.process_upgrade_step(region,
|
||||
|
@ -299,7 +310,43 @@ class SwUpgradeOrchThread(threading.Thread):
|
|||
consts.STRATEGY_STATE_IMPORTING_LOAD:
|
||||
self.process_upgrade_step(region,
|
||||
strategy_step,
|
||||
self.install_subcloud_load,
|
||||
self.import_subcloud_load,
|
||||
log_error=False)
|
||||
elif strategy_step.state == \
|
||||
consts.STRATEGY_STATE_STARTING_UPGRADE:
|
||||
self.process_upgrade_step(region,
|
||||
strategy_step,
|
||||
self.start_subcloud_upgrade,
|
||||
log_error=False)
|
||||
elif strategy_step.state == \
|
||||
consts.STRATEGY_STATE_LOCKING_CONTROLLER:
|
||||
self.process_upgrade_step(region,
|
||||
strategy_step,
|
||||
self.lock_subcloud_controller,
|
||||
log_error=False)
|
||||
elif strategy_step.state == \
|
||||
consts.STRATEGY_STATE_UPGRADING_SIMPLEX:
|
||||
self.process_upgrade_step(region,
|
||||
strategy_step,
|
||||
self.upgrade_subcloud_simplex,
|
||||
log_error=False)
|
||||
elif strategy_step.state == \
|
||||
consts.STRATEGY_STATE_MIGRATING_DATA:
|
||||
self.process_upgrade_step(region,
|
||||
strategy_step,
|
||||
self.migrate_subcloud_data,
|
||||
log_error=False)
|
||||
elif strategy_step.state == \
|
||||
consts.STRATEGY_STATE_UNLOCKING_CONTROLLER:
|
||||
self.process_upgrade_step(region,
|
||||
strategy_step,
|
||||
self.unlock_subcloud_controller,
|
||||
log_error=False)
|
||||
elif strategy_step.state == \
|
||||
consts.STRATEGY_STATE_ACTIVATING:
|
||||
self.process_upgrade_step(region,
|
||||
strategy_step,
|
||||
self.activate_subcloud,
|
||||
log_error=False)
|
||||
# todo(abailey): Add calls to self.process_upgrade_step
|
||||
# for each additional state, with the appropriate thread
|
||||
|
@ -358,27 +405,6 @@ class SwUpgradeOrchThread(threading.Thread):
|
|||
if region in self.subcloud_workers:
|
||||
del self.subcloud_workers[region]
|
||||
|
||||
def install_subcloud_load(self, strategy_step):
|
||||
"""Install the load on the subcloud
|
||||
|
||||
Removes the worker reference after the operation is complete.
|
||||
"""
|
||||
|
||||
try:
|
||||
# self.do_install_subcloud_load(strategy_step)
|
||||
raise NotImplementedError
|
||||
except Exception:
|
||||
# Catch ALL exceptions and set the strategy to failed
|
||||
LOG.exception("Install load failed")
|
||||
self.strategy_step_update(strategy_step.subcloud_id,
|
||||
state=consts.STRATEGY_STATE_FAILED,
|
||||
details=("Install load failed"))
|
||||
finally:
|
||||
# The worker is done.
|
||||
region = self.get_region_name(strategy_step)
|
||||
if region in self.subcloud_workers:
|
||||
del self.subcloud_workers[region]
|
||||
|
||||
def do_install_subcloud_license(self, strategy_step):
|
||||
"""Install the License for a software upgrade in this subcloud"""
|
||||
|
||||
|
@ -402,7 +428,7 @@ class SwUpgradeOrchThread(threading.Thread):
|
|||
target_error = system_controller_license.get('error')
|
||||
|
||||
# If the system controller does not have a license, do not attempt
|
||||
# to install licenses on subcluds, and simply proceed to the next stage
|
||||
# to install licenses on subclouds, and simply proceed to the next stage
|
||||
if len(target_error) != 0:
|
||||
if LICENSE_FILE_NOT_FOUND_SUBSTRING in target_error:
|
||||
LOG.debug("Stage:<%s>, Subcloud:<%s>. "
|
||||
|
@ -421,7 +447,7 @@ class SwUpgradeOrchThread(threading.Thread):
|
|||
self.get_region_name(strategy_step),
|
||||
target_error))
|
||||
raise exceptions.LicenseMissingError(
|
||||
subcloud_id="SystemController")
|
||||
subcloud_id=consts.SYSTEM_CONTROLLER_NAME)
|
||||
|
||||
# retrieve the keystone session for the subcloud and query its license
|
||||
subcloud_ks_client = self.get_ks_client(strategy_step.subcloud.name)
|
||||
|
@ -509,3 +535,74 @@ class SwUpgradeOrchThread(threading.Thread):
|
|||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
raise e
|
||||
|
||||
def perform_state_action(self, strategy_step, state_operator, next_state):
|
||||
"""Extensible state handler for processing and transitioning states """
|
||||
try:
|
||||
LOG.info("Stage: %s, State: %s, Subcloud: %s"
|
||||
% (strategy_step.stage,
|
||||
strategy_step.state,
|
||||
self.get_region_name(strategy_step)))
|
||||
state_operator.perform_state_action(strategy_step)
|
||||
# If we get here without an exception raised, proceed to next state
|
||||
self.strategy_step_update(strategy_step.subcloud_id,
|
||||
state=next_state)
|
||||
except Exception as e:
|
||||
# Catch ALL exceptions and set the strategy to failed
|
||||
LOG.exception("Failed! Stage: %s, State: %s, Subcloud: %s"
|
||||
% (strategy_step.stage,
|
||||
strategy_step.state,
|
||||
self.get_region_name(strategy_step)))
|
||||
details = self.format_update_details(strategy_step.state, str(e))
|
||||
self.strategy_step_update(strategy_step.subcloud_id,
|
||||
state=consts.STRATEGY_STATE_FAILED,
|
||||
details=details)
|
||||
finally:
|
||||
# The worker is done.
|
||||
region = self.get_region_name(strategy_step)
|
||||
if region in self.subcloud_workers:
|
||||
del self.subcloud_workers[region]
|
||||
|
||||
# todo(abailey): convert license install to the same pattern as the other states
|
||||
|
||||
def import_subcloud_load(self, strategy_step):
|
||||
from dcmanager.manager.states.upgrade.import_load import ImportLoadState
|
||||
self.perform_state_action(strategy_step,
|
||||
ImportLoadState(),
|
||||
consts.STRATEGY_STATE_STARTING_UPGRADE)
|
||||
|
||||
def start_subcloud_upgrade(self, strategy_step):
|
||||
from dcmanager.manager.states.upgrade.starting_upgrade import StartingUpgradeState
|
||||
self.perform_state_action(strategy_step,
|
||||
StartingUpgradeState(),
|
||||
consts.STRATEGY_STATE_LOCKING_CONTROLLER)
|
||||
|
||||
def lock_subcloud_controller(self, strategy_step):
|
||||
from dcmanager.manager.states.lock_host import LockHostState
|
||||
self.perform_state_action(strategy_step,
|
||||
LockHostState(),
|
||||
consts.STRATEGY_STATE_UPGRADING_SIMPLEX)
|
||||
|
||||
def upgrade_subcloud_simplex(self, strategy_step):
|
||||
from dcmanager.manager.states.upgrade.upgrading_simplex import UpgradingSimplexState
|
||||
self.perform_state_action(strategy_step,
|
||||
UpgradingSimplexState(),
|
||||
consts.STRATEGY_STATE_MIGRATING_DATA)
|
||||
|
||||
def migrate_subcloud_data(self, strategy_step):
|
||||
from dcmanager.manager.states.upgrade.migrating_data import MigratingDataState
|
||||
self.perform_state_action(strategy_step,
|
||||
MigratingDataState(),
|
||||
consts.STRATEGY_STATE_UNLOCKING_CONTROLLER)
|
||||
|
||||
def unlock_subcloud_controller(self, strategy_step):
|
||||
from dcmanager.manager.states.unlock_host import UnlockHostState
|
||||
self.perform_state_action(strategy_step,
|
||||
UnlockHostState(),
|
||||
consts.STRATEGY_STATE_ACTIVATING)
|
||||
|
||||
def activate_subcloud(self, strategy_step):
|
||||
from dcmanager.manager.states.upgrade.activating import ActivatingState
|
||||
self.perform_state_action(strategy_step,
|
||||
ActivatingState(),
|
||||
consts.STRATEGY_STATE_COMPLETE)
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import mock
|
||||
|
||||
from dcmanager.manager.states.base import BaseState
|
||||
from sysinv.common import constants as sysinv_constants
|
||||
|
||||
from dcmanager.tests.unit.manager.test_sw_upgrade import TestSwUpgrade
|
||||
|
||||
CURRENT_LOAD = '20.01'
|
||||
UPDATED_LOAD = '20.06'
|
||||
|
||||
|
||||
class FakeKeystoneClient(object):
|
||||
def __init__(self):
|
||||
self.session = mock.MagicMock()
|
||||
|
||||
|
||||
class FakeSysinvClient(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class FakeController(object):
|
||||
def __init__(self,
|
||||
host_id=1,
|
||||
hostname='controller-0',
|
||||
administrative=sysinv_constants.ADMIN_UNLOCKED,
|
||||
availability=sysinv_constants.AVAILABILITY_AVAILABLE,
|
||||
ihost_action=None,
|
||||
target_load=CURRENT_LOAD,
|
||||
task=None):
|
||||
self.id = host_id
|
||||
self.hostname = hostname
|
||||
self.administrative = administrative
|
||||
self.availability = availability
|
||||
self.ihost_action = ihost_action
|
||||
self.target_load = target_load
|
||||
self.task = task
|
||||
|
||||
|
||||
class TestSwUpgradeState(TestSwUpgrade):
|
||||
def setUp(self):
|
||||
super(TestSwUpgradeState, self).setUp()
|
||||
|
||||
# Mock the host environment.
|
||||
self.controller_0 = self.fake_controller('controller-0')
|
||||
|
||||
# Mock the keystone client defined in the base upgrade state class
|
||||
self.keystone_client = FakeKeystoneClient()
|
||||
p = mock.patch.object(BaseState, 'get_keystone_client')
|
||||
self.mock_keystone_client = p.start()
|
||||
self.mock_keystone_client.return_value = self.keystone_client
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
# Mock the sysinv client defined in the base upgrade state class
|
||||
self.sysinv_client = FakeSysinvClient()
|
||||
p = mock.patch.object(BaseState, 'get_sysinv_client')
|
||||
self.mock_sysinv_client = p.start()
|
||||
self.mock_sysinv_client.return_value = self.sysinv_client
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def fake_controller(self, hostname):
|
||||
return FakeController(hostname=hostname)
|
|
@ -0,0 +1,146 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import itertools
|
||||
import mock
|
||||
|
||||
from dcmanager.common import consts
|
||||
from dcmanager.manager.states.lock_host import DEFAULT_MAX_QUERIES
|
||||
|
||||
from dcmanager.tests.unit.manager.states.upgrade.test_base \
|
||||
import FakeController
|
||||
from dcmanager.tests.unit.manager.states.upgrade.test_base \
|
||||
import TestSwUpgradeState
|
||||
|
||||
CONTROLLER_0_UNLOCKED = FakeController(administrative=consts.ADMIN_UNLOCKED)
|
||||
CONTROLLER_0_LOCKED = FakeController(administrative=consts.ADMIN_LOCKED)
|
||||
CONTROLLER_0_LOCKING = FakeController(administrative=consts.ADMIN_UNLOCKED,
|
||||
ihost_action='lock',
|
||||
task='Locking')
|
||||
CONTROLLER_0_LOCKING_FAILED = \
|
||||
FakeController(administrative=consts.ADMIN_UNLOCKED,
|
||||
ihost_action='force-swact',
|
||||
task='Swacting')
|
||||
|
||||
|
||||
class TestSwUpgradeLockControllerStage(TestSwUpgradeState):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSwUpgradeLockControllerStage, self).setUp()
|
||||
|
||||
# next state after a successful lock is upgrading simplex
|
||||
self.on_success_state = consts.STRATEGY_STATE_UPGRADING_SIMPLEX
|
||||
|
||||
# Add the strategy_step state being processed by this unit test
|
||||
self.strategy_step = \
|
||||
self.setup_strategy_step(consts.STRATEGY_STATE_LOCKING_CONTROLLER)
|
||||
|
||||
# Add mock API endpoints for sysinv client calls invcked by this state
|
||||
self.sysinv_client.get_host = mock.MagicMock()
|
||||
self.sysinv_client.lock_host = mock.MagicMock()
|
||||
|
||||
def test_lock_success(self):
|
||||
"""Test the lock command returns a success"""
|
||||
|
||||
# mock the controller host queries
|
||||
# first query is the starting state
|
||||
# query 2,3 are are during the lock phase
|
||||
# query 4 : the host is now locked
|
||||
self.sysinv_client.get_host.side_effect = [CONTROLLER_0_UNLOCKED,
|
||||
CONTROLLER_0_LOCKING,
|
||||
CONTROLLER_0_LOCKING,
|
||||
CONTROLLER_0_LOCKED]
|
||||
|
||||
# mock the API call as failed on the subcloud
|
||||
self.sysinv_client.lock_host.return_value = CONTROLLER_0_LOCKING
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.lock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the lock command was actually attempted
|
||||
self.sysinv_client.lock_host.assert_called()
|
||||
|
||||
# verify that the API moved to the next state on success
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
self.on_success_state)
|
||||
|
||||
def test_lock_skipped_when_already_locked(self):
|
||||
"""Test the lock command skips if host is already locked"""
|
||||
|
||||
# mock the controller host query as being already locked
|
||||
self.sysinv_client.get_host.return_value = CONTROLLER_0_LOCKED
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.lock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the lock command was never attempted
|
||||
self.sysinv_client.lock_host.assert_not_called()
|
||||
|
||||
# verify that the state moves to the next state
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
self.on_success_state)
|
||||
|
||||
def test_lock_attempt_timeout(self):
|
||||
"""Test lock invoked and fails if timeout before host becomes locked"""
|
||||
|
||||
# mock the get_host queries
|
||||
# first query is the starting state
|
||||
# all remaining queries, the host returns 'locking'
|
||||
self.sysinv_client.get_host.side_effect = itertools.chain(
|
||||
[CONTROLLER_0_UNLOCKED, ],
|
||||
itertools.repeat(CONTROLLER_0_LOCKING))
|
||||
|
||||
# mock the API call as successful on the subcloud
|
||||
self.sysinv_client.lock_host.return_value = CONTROLLER_0_LOCKING
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.lock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the lock command was actually attempted
|
||||
self.sysinv_client.lock_host.assert_called()
|
||||
|
||||
# verify the query was invoked: 1 + max_attempts times
|
||||
self.assertEqual(DEFAULT_MAX_QUERIES + 1,
|
||||
self.sysinv_client.get_host.call_count)
|
||||
|
||||
# verify that state failed due to subcloud never finishing the lock
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_FAILED)
|
||||
|
||||
def test_lock_failure(self):
|
||||
"""Test the lock command returns a failure"""
|
||||
|
||||
# mock the controller get_host query
|
||||
self.sysinv_client.get_host.return_value = CONTROLLER_0_UNLOCKED
|
||||
|
||||
# mock the API call as failed on the subcloud
|
||||
self.sysinv_client.lock_host.return_value = CONTROLLER_0_LOCKING_FAILED
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.lock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the lock command was actually attempted
|
||||
self.sysinv_client.lock_host.assert_called()
|
||||
|
||||
# verify that the API error for the lock leads to a failed state
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_FAILED)
|
||||
|
||||
def test_lock_fails_when_host_query_fails(self):
|
||||
"""Test the lock command fails when it cannot get the controllers"""
|
||||
|
||||
# mock the get_host query is empty and raises an exception
|
||||
self.sysinv_client.get_host.side_effect = \
|
||||
Exception("Unable to find host controller-0")
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.lock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the lock command was never attempted
|
||||
self.sysinv_client.lock_host.assert_not_called()
|
||||
|
||||
# verify that the state moves to the next state
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_FAILED)
|
|
@ -0,0 +1,147 @@
|
|||
#
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import itertools
|
||||
import mock
|
||||
|
||||
from dcmanager.common import consts
|
||||
from dcmanager.manager.states.unlock_host import DEFAULT_MAX_QUERIES
|
||||
|
||||
from dcmanager.tests.unit.manager.states.upgrade.test_base \
|
||||
import FakeController
|
||||
from dcmanager.tests.unit.manager.states.upgrade.test_base \
|
||||
import TestSwUpgradeState
|
||||
|
||||
CONTROLLER_0_UNLOCKED = FakeController(administrative=consts.ADMIN_UNLOCKED)
|
||||
CONTROLLER_0_LOCKED = FakeController(administrative=consts.ADMIN_LOCKED)
|
||||
CONTROLLER_0_UNLOCKING = FakeController(administrative=consts.ADMIN_LOCKED,
|
||||
ihost_action='unlock',
|
||||
task='Unlocking')
|
||||
CONTROLLER_0_UNLOCKING_FAILED = \
|
||||
FakeController(administrative=consts.ADMIN_LOCKED,
|
||||
ihost_action='force-swact',
|
||||
task='Swacting')
|
||||
|
||||
|
||||
class TestSwUpgradeUnlockControllerStage(TestSwUpgradeState):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSwUpgradeUnlockControllerStage, self).setUp()
|
||||
|
||||
# next state after a successful unlock is 'activating'
|
||||
self.on_success_state = consts.STRATEGY_STATE_ACTIVATING
|
||||
|
||||
# Add the strategy_step state being processed by this unit test
|
||||
self.strategy_step = self.setup_strategy_step(
|
||||
consts.STRATEGY_STATE_UNLOCKING_CONTROLLER)
|
||||
|
||||
# Add mock API endpoints for sysinv client calls invcked by this state
|
||||
self.sysinv_client.get_host = mock.MagicMock()
|
||||
self.sysinv_client.unlock_host = mock.MagicMock()
|
||||
|
||||
def test_unlock_success(self):
|
||||
"""Test the unlock command returns a success"""
|
||||
|
||||
# mock the get_host queries
|
||||
# first query is the starting state
|
||||
# query 2,3 are are during the unlock phase
|
||||
# query 4 : the host is now unlocked
|
||||
self.sysinv_client.get_host.side_effect = [CONTROLLER_0_LOCKED,
|
||||
CONTROLLER_0_UNLOCKING,
|
||||
CONTROLLER_0_UNLOCKING,
|
||||
CONTROLLER_0_UNLOCKED, ]
|
||||
|
||||
# mock the API call as failed on the subcloud
|
||||
self.sysinv_client.unlock_host.return_value = CONTROLLER_0_UNLOCKING
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.unlock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the unlock command was actually attempted
|
||||
self.sysinv_client.unlock_host.assert_called()
|
||||
|
||||
# verify that the API moved to the next state on success
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
self.on_success_state)
|
||||
|
||||
def test_unlock_skipped_when_already_unlocked(self):
|
||||
"""Test the unlock command skips if host is already unlocked"""
|
||||
|
||||
# mock the controller host query as being already unlocked
|
||||
self.sysinv_client.get_host.return_value = CONTROLLER_0_UNLOCKED
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.unlock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the unlock command was never attempted
|
||||
self.sysinv_client.unlock_host.assert_not_called()
|
||||
|
||||
# verify that the state moves to the next state
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
self.on_success_state)
|
||||
|
||||
def test_unlock_attempt_timeout(self):
|
||||
"""Test unlock invoked handles timeout if unlocking takes too long"""
|
||||
|
||||
# mock the get_host queries
|
||||
# first query is the starting state
|
||||
# all remaining queries, the host returns 'unlocking'
|
||||
self.sysinv_client.get_host.side_effect = itertools.chain(
|
||||
[CONTROLLER_0_LOCKED, ],
|
||||
itertools.repeat(CONTROLLER_0_UNLOCKING))
|
||||
|
||||
# mock the API call as successful on the subcloud
|
||||
self.sysinv_client.unlock_host.return_value = CONTROLLER_0_UNLOCKING
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.unlock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the lock command was actually attempted
|
||||
self.sysinv_client.unlock_host.assert_called()
|
||||
|
||||
# verify the query was invoked: 1 + max_attempts times
|
||||
self.assertEqual(DEFAULT_MAX_QUERIES + 1,
|
||||
self.sysinv_client.get_host.call_count)
|
||||
|
||||
# verify that state failed due to subcloud never finishing the unlock
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_FAILED)
|
||||
|
||||
def test_unlock_failure(self):
|
||||
"""Test the unlock command returns a failure"""
|
||||
|
||||
# mock the get_host query
|
||||
self.sysinv_client.get_host.return_value = CONTROLLER_0_LOCKED
|
||||
|
||||
# mock the API call as failed on the subcloud
|
||||
self.sysinv_client.unlock_host.return_value = \
|
||||
CONTROLLER_0_UNLOCKING_FAILED
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.unlock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the unlock command was actually attempted
|
||||
self.sysinv_client.unlock_host.assert_called()
|
||||
|
||||
# verify that the API error for the unlock leads to a failed state
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_FAILED)
|
||||
|
||||
def test_unlock_fails_when_host_query_fails(self):
|
||||
"""Test the unlock command fails when it cannot get the controllers"""
|
||||
|
||||
# mock the get_host query fails and raises an exception
|
||||
self.sysinv_client.get_host.side_effect = \
|
||||
Exception("Unable to find host controller-0")
|
||||
|
||||
# invoke the strategy state operation
|
||||
self.worker.unlock_subcloud_controller(self.strategy_step)
|
||||
|
||||
# verify the unlock command was never attempted
|
||||
self.sysinv_client.unlock_host.assert_not_called()
|
||||
|
||||
# verify that the state moves to the next state
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_FAILED)
|
|
@ -10,7 +10,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copyright (c) 2017 Wind River Systems, Inc.
|
||||
# Copyright (c) 2017-2020 Wind River Systems, Inc.
|
||||
#
|
||||
# The right to copy, distribute, modify, or otherwise make use
|
||||
# of this software may be licensed only pursuant to the terms
|
||||
|
@ -112,6 +112,16 @@ class TestSwUpgrade(base.DCManagerTestCase):
|
|||
self.mock_db_api = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def setup_strategy_step(self, strategy_state):
|
||||
data = copy.copy(FAKE_STRATEGY_STEP_DATA)
|
||||
data['state'] = strategy_state
|
||||
data['subcloud'] = Subcloud(1,
|
||||
'subcloud1',
|
||||
is_managed=True,
|
||||
is_online=True)
|
||||
fake_strategy_step = StrategyStep(**data)
|
||||
return fake_strategy_step
|
||||
|
||||
def setup_upgrade_worker(self):
|
||||
sw_update_manager.SwUpgradeOrchThread.stopped = lambda x: False
|
||||
mock_strategy_lock = mock.Mock()
|
||||
|
@ -136,16 +146,8 @@ class TestSwUpgradeLicenseStage(TestSwUpgrade):
|
|||
|
||||
def setUp(self):
|
||||
super(TestSwUpgradeLicenseStage, self).setUp()
|
||||
|
||||
def setup_license_install_step(self):
|
||||
data = copy.copy(FAKE_STRATEGY_STEP_DATA)
|
||||
data['state'] = consts.STRATEGY_STATE_INSTALLING_LICENSE
|
||||
data['subcloud'] = Subcloud(1,
|
||||
'subcloud1',
|
||||
is_managed=True,
|
||||
is_online=True)
|
||||
fake_strategy_step = StrategyStep(**data)
|
||||
return fake_strategy_step
|
||||
self.strategy_step = \
|
||||
self.setup_strategy_step(consts.STRATEGY_STATE_INSTALLING_LICENSE)
|
||||
|
||||
def test_upgrade_subcloud_license_install_failure(self):
|
||||
# Test the install subcloud license step where the system controller
|
||||
|
@ -162,14 +164,13 @@ class TestSwUpgradeLicenseStage(TestSwUpgrade):
|
|||
self.fake_sysinv_client.install_license.return_value = \
|
||||
MISSING_LICENSE_RESPONSE
|
||||
|
||||
fake_strategy_step = self.setup_license_install_step()
|
||||
self.worker.install_subcloud_license(fake_strategy_step)
|
||||
self.worker.install_subcloud_license(self.strategy_step)
|
||||
|
||||
# verify the license install was invoked
|
||||
self.fake_sysinv_client.install_license.assert_called()
|
||||
|
||||
# Verify a install_license failure leads to a state failure
|
||||
self.assert_step_updated(fake_strategy_step.subcloud_id,
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_FAILED)
|
||||
|
||||
def test_upgrade_subcloud_license_install_success(self):
|
||||
|
@ -187,14 +188,13 @@ class TestSwUpgradeLicenseStage(TestSwUpgrade):
|
|||
self.fake_sysinv_client.install_license.return_value = \
|
||||
LICENSE_VALID_RESPONSE
|
||||
|
||||
fake_strategy_step = self.setup_license_install_step()
|
||||
self.worker.install_subcloud_license(fake_strategy_step)
|
||||
self.worker.install_subcloud_license(self.strategy_step)
|
||||
|
||||
# verify the license install was invoked
|
||||
self.fake_sysinv_client.install_license.assert_called()
|
||||
|
||||
# On success, the next state after installing license is importing load
|
||||
self.assert_step_updated(fake_strategy_step.subcloud_id,
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_IMPORTING_LOAD)
|
||||
|
||||
def test_upgrade_subcloud_license_skip_existing(self):
|
||||
|
@ -207,14 +207,13 @@ class TestSwUpgradeLicenseStage(TestSwUpgrade):
|
|||
self.fake_sysinv_client.get_license.side_effect = \
|
||||
[LICENSE_VALID_RESPONSE,
|
||||
LICENSE_VALID_RESPONSE]
|
||||
fake_strategy_step = self.setup_license_install_step()
|
||||
self.worker.install_subcloud_license(fake_strategy_step)
|
||||
self.worker.install_subcloud_license(self.strategy_step)
|
||||
|
||||
# A license install should not have been attempted due to the license
|
||||
# already being up to date
|
||||
self.fake_sysinv_client.install_license.assert_not_called()
|
||||
# On success, the next state after installing license is importing load
|
||||
self.assert_step_updated(fake_strategy_step.subcloud_id,
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_IMPORTING_LOAD)
|
||||
|
||||
def test_upgrade_subcloud_license_overrides_mismatched_license(self):
|
||||
|
@ -233,14 +232,13 @@ class TestSwUpgradeLicenseStage(TestSwUpgrade):
|
|||
self.fake_sysinv_client.install_license.return_value = \
|
||||
LICENSE_VALID_RESPONSE
|
||||
|
||||
fake_strategy_step = self.setup_license_install_step()
|
||||
self.worker.install_subcloud_license(fake_strategy_step)
|
||||
self.worker.install_subcloud_license(self.strategy_step)
|
||||
|
||||
# verify the license install was invoked
|
||||
self.fake_sysinv_client.install_license.assert_called()
|
||||
|
||||
# On success, the next state after installing license is importing load
|
||||
self.assert_step_updated(fake_strategy_step.subcloud_id,
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_IMPORTING_LOAD)
|
||||
|
||||
def test_upgrade_subcloud_license_skip_when_no_sys_controller_lic(self):
|
||||
|
@ -250,15 +248,14 @@ class TestSwUpgradeLicenseStage(TestSwUpgrade):
|
|||
# Only makes one query to system controller
|
||||
self.fake_sysinv_client.get_license.side_effect = \
|
||||
[MISSING_LICENSE_RESPONSE, ]
|
||||
fake_strategy_step = self.setup_license_install_step()
|
||||
# Test the install subcloud license stage
|
||||
self.worker.install_subcloud_license(fake_strategy_step)
|
||||
self.worker.install_subcloud_license(self.strategy_step)
|
||||
|
||||
# A license install should proceed to the next state without
|
||||
# calling a license install
|
||||
self.fake_sysinv_client.install_license.assert_not_called()
|
||||
# Skip license install and move to next state
|
||||
self.assert_step_updated(fake_strategy_step.subcloud_id,
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_IMPORTING_LOAD)
|
||||
|
||||
def test_upgrade_subcloud_license_handle_failure(self):
|
||||
|
@ -276,14 +273,13 @@ class TestSwUpgradeLicenseStage(TestSwUpgrade):
|
|||
self.fake_sysinv_client.install_license.return_value = \
|
||||
MISSING_LICENSE_RESPONSE
|
||||
|
||||
fake_strategy_step = self.setup_license_install_step()
|
||||
self.worker.install_subcloud_license(fake_strategy_step)
|
||||
self.worker.install_subcloud_license(self.strategy_step)
|
||||
|
||||
# verify the license install was invoked
|
||||
self.fake_sysinv_client.install_license.assert_called()
|
||||
|
||||
# Verify a install_license failure leads to a state failure
|
||||
self.assert_step_updated(fake_strategy_step.subcloud_id,
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_FAILED)
|
||||
|
||||
def test_upgrade_subcloud_license_installs(self):
|
||||
|
@ -301,12 +297,11 @@ class TestSwUpgradeLicenseStage(TestSwUpgrade):
|
|||
self.fake_sysinv_client.install_license.return_value = \
|
||||
LICENSE_VALID_RESPONSE
|
||||
|
||||
fake_strategy_step = self.setup_license_install_step()
|
||||
self.worker.install_subcloud_license(fake_strategy_step)
|
||||
self.worker.install_subcloud_license(self.strategy_step)
|
||||
|
||||
# verify the license install was invoked
|
||||
self.fake_sysinv_client.install_license.assert_called()
|
||||
|
||||
# On success, the next state after installing license is importing load
|
||||
self.assert_step_updated(fake_strategy_step.subcloud_id,
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||
consts.STRATEGY_STATE_IMPORTING_LOAD)
|
||||
|
|
Loading…
Reference in New Issue