From c640a5144011b25a76637ce3bb6cb59b16d35def Mon Sep 17 00:00:00 2001 From: albailey Date: Tue, 14 Apr 2020 15:00:01 -0500 Subject: [PATCH] Adding upgrade strategy commands dcmanager strategy-config dcmanager upgrade-strategy This change refactors the existing sw-update code so that patch and upgrades share common functionality. This adds the strategy 'type' to the CLI output so that a user can differentiate between which strategy type is being orchestrated. patch-strategy-config is generic to all update types, so a new strategy-config CLI command is now registered. Change-Id: Icd33eb26f907e8e250ebddbba7d2cebea3592ac7 Depends-On: https://review.opendev.org/#/c/721620 Story: 2007403 Task: 39654 Signed-off-by: albailey --- .../dcmanagerclient/api/v1/client.py | 21 +- .../api/v1/fw_update_manager.py | 32 +++ .../api/v1/strategy_step_manager.py | 87 ++++++++ .../api/v1/sw_patch_manager.py | 31 +++ .../api/v1/sw_update_manager.py | 193 ++++++------------ .../api/v1/sw_upgrade_manager.py | 32 +++ .../commands/v1/fw_update_manager.py | 59 ++++++ .../commands/v1/sw_patch_manager.py | 59 ++++++ .../commands/v1/sw_update_manager.py | 120 +++++------ .../commands/v1/sw_upgrade_manager.py | 59 ++++++ .../dcmanagerclient/shell.py | 37 +++- .../tests/v1/test_step_manager.py | 68 ++++++ 12 files changed, 592 insertions(+), 206 deletions(-) create mode 100644 distributedcloud-client/dcmanagerclient/api/v1/fw_update_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/api/v1/strategy_step_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/api/v1/sw_patch_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/api/v1/sw_upgrade_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/commands/v1/fw_update_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/commands/v1/sw_patch_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/commands/v1/sw_upgrade_manager.py create mode 100644 distributedcloud-client/dcmanagerclient/tests/v1/test_step_manager.py diff --git a/distributedcloud-client/dcmanagerclient/api/v1/client.py b/distributedcloud-client/dcmanagerclient/api/v1/client.py index 61ae58b..9b955c5 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/client.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/client.py @@ -20,23 +20,22 @@ # of this software may be licensed only pursuant to the terms # of an applicable Wind River license agreement. # +import six import keystoneauth1.identity.generic as auth_plugin from keystoneauth1 import session as ks_session +import osprofiler.profiler from dcmanagerclient.api import httpclient from dcmanagerclient.api.v1 import alarm_manager as am +from dcmanagerclient.api.v1 import fw_update_manager as fum +from dcmanagerclient.api.v1 import strategy_step_manager as ssm from dcmanagerclient.api.v1 import subcloud_deploy_manager as sdm from dcmanagerclient.api.v1 import subcloud_group_manager as gm from dcmanagerclient.api.v1 import subcloud_manager as sm -from dcmanagerclient.api.v1 import sw_update_manager as sum +from dcmanagerclient.api.v1 import sw_patch_manager as spm from dcmanagerclient.api.v1 import sw_update_options_manager as suom - - -import osprofiler.profiler - -import six - +from dcmanagerclient.api.v1 import sw_upgrade_manager as supm _DEFAULT_DCMANAGER_URL = "http://localhost:8119/v1.0" @@ -102,11 +101,13 @@ class Client(object): self.subcloud_deploy_manager = sdm.subcloud_deploy_manager( self.http_client) self.alarm_manager = am.alarm_manager(self.http_client) - self.sw_update_manager = sum.sw_update_manager(self.http_client) + self.fw_update_manager = fum.fw_update_manager(self.http_client) + self.sw_patch_manager = spm.sw_patch_manager(self.http_client) self.sw_update_options_manager = \ suom.sw_update_options_manager(self.http_client) - self.strategy_step_manager = sum.strategy_step_manager( - self.http_client) + self.sw_upgrade_manager = supm.sw_upgrade_manager(self.http_client) + self.strategy_step_manager = \ + ssm.strategy_step_manager(self.http_client) def authenticate(dcmanager_url=None, username=None, diff --git a/distributedcloud-client/dcmanagerclient/api/v1/fw_update_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/fw_update_manager.py new file mode 100644 index 0000000..ab6fed9 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/api/v1/fw_update_manager.py @@ -0,0 +1,32 @@ +# Copyright (c) 2017 Ericsson AB. +# 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. +# +# Copyright (c) 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 +# of an applicable Wind River license agreement. +# +from dcmanagerclient.api.v1.sw_update_manager import sw_update_manager + +SW_UPDATE_TYPE_FIRMWARE = 'firmware' + + +class fw_update_manager(sw_update_manager): + + def __init__(self, http_client): + super(fw_update_manager, self).__init__( + http_client, + update_type=SW_UPDATE_TYPE_FIRMWARE) diff --git a/distributedcloud-client/dcmanagerclient/api/v1/strategy_step_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/strategy_step_manager.py new file mode 100644 index 0000000..50ab402 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/api/v1/strategy_step_manager.py @@ -0,0 +1,87 @@ +# Copyright (c) 2017 Ericsson AB. +# 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. +# +# 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 +# of an applicable Wind River license agreement. +# +from dcmanagerclient.api import base +from dcmanagerclient.api.base import get_json + + +class StrategyStep(base.Resource): + resource_name = 'strategy_step' + + def __init__(self, manager, cloud, stage, state, details, + started_at, finished_at, created_at, updated_at): + self.manager = manager + self.cloud = cloud + self.stage = stage + self.state = state + self.details = details + self.started_at = started_at + self.finished_at = finished_at + self.created_at = created_at + self.updated_at = updated_at + + +class strategy_step_manager(base.ResourceManager): + + def __init__(self, http_client): + super(strategy_step_manager, self).__init__(http_client) + self.resource_class = StrategyStep + self.steps_url = '/sw-update-strategy/steps' + self.response_key = 'strategy-steps' + + def list_strategy_steps(self): + return self._strategy_step_list(self.steps_url) + + def strategy_step_detail(self, cloud_name): + url = '{}/{}'.format(self.steps_url, cloud_name) + return self._strategy_step_detail(url) + + def build_from_json(self, json_object): + return self.resource_class( + self, + cloud=json_object['cloud'], + stage=json_object['stage'], + state=json_object['state'], + details=json_object['details'], + started_at=json_object['started-at'], + finished_at=json_object['finished-at'], + created_at=json_object['created-at'], + updated_at=json_object['updated-at']) + + def _strategy_step_list(self, url): + resp = self.http_client.get(url) + if resp.status_code != 200: + self._raise_api_exception(resp) + json_response_key = get_json(resp) + json_objects = json_response_key[self.response_key] + resource = [] + for json_object in json_objects: + resource.append(self.build_from_json(json_object)) + return resource + + def _strategy_step_detail(self, url): + resp = self.http_client.get(url) + if resp.status_code != 200: + self._raise_api_exception(resp) + json_object = get_json(resp) + resource = list() + resource.append(self.build_from_json(json_object)) + return resource diff --git a/distributedcloud-client/dcmanagerclient/api/v1/sw_patch_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/sw_patch_manager.py new file mode 100644 index 0000000..71213d2 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/api/v1/sw_patch_manager.py @@ -0,0 +1,31 @@ +# Copyright (c) 2017 Ericsson AB. +# 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. +# +# Copyright (c) 2017 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 +# of an applicable Wind River license agreement. +# +from dcmanagerclient.api.v1.sw_update_manager import sw_update_manager + +SW_UPDATE_TYPE_PATCH = 'patch' + + +class sw_patch_manager(sw_update_manager): + def __init__(self, http_client): + super(sw_patch_manager, self).__init__( + http_client, + update_type=SW_UPDATE_TYPE_PATCH) diff --git a/distributedcloud-client/dcmanagerclient/api/v1/sw_update_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/sw_update_manager.py index e5f1364..d910128 100644 --- a/distributedcloud-client/dcmanagerclient/api/v1/sw_update_manager.py +++ b/distributedcloud-client/dcmanagerclient/api/v1/sw_update_manager.py @@ -26,13 +26,21 @@ from dcmanagerclient.api import base from dcmanagerclient.api.base import get_json +# todo(abailey): Update SwUpdateStrategy based on 'subcloud group' class SwUpdateStrategy(base.Resource): resource_name = 'sw_update_strategy' - def __init__(self, manager, subcloud_apply_type, max_parallel_subclouds, - stop_on_failure, state, - created_at, updated_at): + def __init__(self, + manager, + strategy_type, + subcloud_apply_type, + max_parallel_subclouds, + stop_on_failure, + state, + created_at, + updated_at): self.manager = manager + self.strategy_type = strategy_type self.subcloud_apply_type = subcloud_apply_type self.max_parallel_subclouds = max_parallel_subclouds self.stop_on_failure = stop_on_failure @@ -41,169 +49,94 @@ class SwUpdateStrategy(base.Resource): self.updated_at = updated_at -class StrategyStep(base.Resource): - resource_name = 'strategy_step' - - def __init__(self, manager, cloud, stage, state, details, - started_at, finished_at, created_at, updated_at): - self.manager = manager - self.cloud = cloud - self.stage = stage - self.state = state - self.details = details - self.started_at = started_at - self.finished_at = finished_at - self.created_at = created_at - self.updated_at = updated_at - - class sw_update_manager(base.ResourceManager): - resource_class = SwUpdateStrategy + """sw_update_managea - def create_patch_strategy(self, **kwargs): + sw_update_manager is an abstract class that is used by subclasses to + manage API actions for specific update strategy types such as software + patches and firmware updates. + """ + + def __init__(self, http_client, + update_type, + resource_class=SwUpdateStrategy, + url='sw-update-strategy'): + super(sw_update_manager, self).__init__(http_client) + self.resource_class = resource_class + self.update_type = update_type + # create_url is typically // + self.create_url = '/{}/'.format(url) + # get_url is typically / + self.get_url = '/{}'.format(url) + # delete_url is typically / (same as get) + self.delete_url = '/{}'.format(url) + # actions_url is typically //actions + self.actions_url = '/{}/actions'.format(url) + + def create_sw_update_strategy(self, **kwargs): data = kwargs - data.update({'type': 'patch'}) - url = '/sw-update-strategy/' - return self.sw_update_create(url, data) + data.update({'type': self.update_type}) + return self._sw_update_create(self.create_url, data) - def patch_strategy_detail(self): - url = '/sw-update-strategy' - return self.sw_update_detail(url) + def update_sw_strategy_detail(self): + return self._sw_update_detail(self.get_url) - def delete_patch_strategy(self): - url = '/sw-update-strategy' - return self.sw_update_delete(url) + def delete_sw_update_strategy(self): + return self._sw_update_delete(self.delete_url) - def apply_patch_strategy(self): + def apply_sw_update_strategy(self): data = {'action': 'apply'} - url = '/sw-update-strategy/actions' - return self.sw_update_action(url, data) + return self._sw_update_action(self.actions_url, data) - def abort_patch_strategy(self): + def abort_sw_update_strategy(self): data = {'action': 'abort'} - url = '/sw-update-strategy/actions' - return self.sw_update_action(url, data) + return self._sw_update_action(self.actions_url, data) - def sw_update_create(self, url, data): + def _build_from_json(self, json_object): + return self.resource_class( + self, + strategy_type=json_object['type'], + subcloud_apply_type=json_object['subcloud-apply-type'], + max_parallel_subclouds=json_object['max-parallel-subclouds'], + stop_on_failure=json_object['stop-on-failure'], + state=json_object['state'], + created_at=json_object['created-at'], + updated_at=json_object['updated-at']) + + def _sw_update_create(self, url, data): data = json.dumps(data) resp = self.http_client.post(url, data) if resp.status_code != 200: self._raise_api_exception(resp) json_object = get_json(resp) resource = list() - resource.append( - self.resource_class( - self, - subcloud_apply_type=json_object['subcloud-apply-type'], - max_parallel_subclouds=json_object['max-parallel-subclouds'], - stop_on_failure=json_object['stop-on-failure'], - state=json_object['state'], - created_at=json_object['created-at'], - updated_at=json_object['updated-at'])) + resource.append(self._build_from_json(json_object)) return resource - def sw_update_delete(self, url): + def _sw_update_delete(self, url): resp = self.http_client.delete(url) if resp.status_code != 200: self._raise_api_exception(resp) json_object = get_json(resp) resource = list() - resource.append( - self.resource_class( - self, - subcloud_apply_type=json_object['subcloud-apply-type'], - max_parallel_subclouds=json_object['max-parallel-subclouds'], - stop_on_failure=json_object['stop-on-failure'], - state=json_object['state'], - created_at=json_object['created-at'], - updated_at=json_object['updated-at'])) + resource.append(self._build_from_json(json_object)) return resource - def sw_update_detail(self, url): + def _sw_update_detail(self, url): resp = self.http_client.get(url) if resp.status_code != 200: self._raise_api_exception(resp) json_object = get_json(resp) resource = list() - resource.append( - self.resource_class( - self, - subcloud_apply_type=json_object['subcloud-apply-type'], - max_parallel_subclouds=json_object['max-parallel-subclouds'], - stop_on_failure=json_object['stop-on-failure'], - state=json_object['state'], - created_at=json_object['created-at'], - updated_at=json_object['updated-at'])) + resource.append(self._build_from_json(json_object)) return resource - def sw_update_action(self, url, data): + def _sw_update_action(self, url, data): data = json.dumps(data) resp = self.http_client.post(url, data) if resp.status_code != 200: self._raise_api_exception(resp) json_object = get_json(resp) resource = list() - resource.append( - self.resource_class( - self, - subcloud_apply_type=json_object['subcloud-apply-type'], - max_parallel_subclouds=json_object['max-parallel-subclouds'], - stop_on_failure=json_object['stop-on-failure'], - state=json_object['state'], - created_at=json_object['created-at'], - updated_at=json_object['updated-at'])) - return resource - - -class strategy_step_manager(base.ResourceManager): - resource_class = StrategyStep - - def list_strategy_steps(self): - url = '/sw-update-strategy/steps' - return self.strategy_step_list(url) - - def strategy_step_detail(self, cloud_name): - url = '/sw-update-strategy/steps/%s' % cloud_name - return self._strategy_step_detail(url) - - def strategy_step_list(self, url): - resp = self.http_client.get(url) - if resp.status_code != 200: - self._raise_api_exception(resp) - json_response_key = get_json(resp) - json_objects = json_response_key['strategy-steps'] - resource = [] - for json_object in json_objects: - resource.append( - self.resource_class( - self, - cloud=json_object['cloud'], - stage=json_object['stage'], - state=json_object['state'], - details=json_object['details'], - started_at=json_object['started-at'], - finished_at=json_object['finished-at'], - created_at=json_object['created-at'], - updated_at=json_object['updated-at'], - )) - return resource - - def _strategy_step_detail(self, url): - resp = self.http_client.get(url) - if resp.status_code != 200: - self._raise_api_exception(resp) - json_object = get_json(resp) - resource = list() - resource.append( - self.resource_class( - self, - cloud=json_object['cloud'], - stage=json_object['stage'], - state=json_object['state'], - details=json_object['details'], - started_at=json_object['started-at'], - finished_at=json_object['finished-at'], - created_at=json_object['created-at'], - updated_at=json_object['updated-at'], - )) + resource.append(self._build_from_json(json_object)) return resource diff --git a/distributedcloud-client/dcmanagerclient/api/v1/sw_upgrade_manager.py b/distributedcloud-client/dcmanagerclient/api/v1/sw_upgrade_manager.py new file mode 100644 index 0000000..542393f --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/api/v1/sw_upgrade_manager.py @@ -0,0 +1,32 @@ +# Copyright (c) 2017 Ericsson AB. +# 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. +# +# Copyright (c) 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 +# of an applicable Wind River license agreement. +# +from dcmanagerclient.api.v1.sw_update_manager import sw_update_manager + +SW_UPDATE_TYPE_UPGRADE = 'upgrade' + + +class sw_upgrade_manager(sw_update_manager): + + def __init__(self, http_client): + super(sw_upgrade_manager, self).__init__( + http_client, + update_type=SW_UPDATE_TYPE_UPGRADE) diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/fw_update_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/fw_update_manager.py new file mode 100644 index 0000000..6d8a803 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/commands/v1/fw_update_manager.py @@ -0,0 +1,59 @@ +# Copyright (c) 2017 Ericsson AB. +# +# 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. +# +# Copyright (c) 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 +# of an applicable Wind River license agreement. +# +from dcmanagerclient.commands.v1 import sw_update_manager + + +class FwUpdateManagerMixin(object): + """This Mixin provides the update manager used for firmware updates.""" + + def get_sw_update_manager(self): + dcmanager_client = self.app.client_manager.fw_update_manager + return dcmanager_client.fw_update_manager + + +class CreateFwUpdateStrategy(FwUpdateManagerMixin, + sw_update_manager.CreateSwUpdateStrategy): + """Create a firmware update strategy.""" + pass + + +class ShowFwUpdateStrategy(FwUpdateManagerMixin, + sw_update_manager.ShowSwUpdateStrategy): + """Show the details of a firmware update strategy for a subcloud.""" + pass + + +class DeleteFwUpdateStrategy(FwUpdateManagerMixin, + sw_update_manager.DeleteSwUpdateStrategy): + """Delete firmware update strategy from the database.""" + pass + + +class ApplyFwUpdateStrategy(FwUpdateManagerMixin, + sw_update_manager.ApplySwUpdateStrategy): + """Apply a firmware update strategy.""" + pass + + +class AbortFwUpdateStrategy(FwUpdateManagerMixin, + sw_update_manager.AbortSwUpdateStrategy): + """Abort a firmware update strategy.""" + pass diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/sw_patch_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/sw_patch_manager.py new file mode 100644 index 0000000..9e0c1f6 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/commands/v1/sw_patch_manager.py @@ -0,0 +1,59 @@ +# Copyright (c) 2017 Ericsson AB. +# +# 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. +# +# 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 +# of an applicable Wind River license agreement. +# +from dcmanagerclient.commands.v1 import sw_update_manager + + +class SwPatchManagerMixin(object): + """This Mixin provides the update manager used for sw patch.""" + + def get_sw_update_manager(self): + dcmanager_client = self.app.client_manager.sw_patch_manager + return dcmanager_client.sw_patch_manager + + +class CreatePatchUpdateStrategy(SwPatchManagerMixin, + sw_update_manager.CreateSwUpdateStrategy): + """Create a patch update strategy.""" + pass + + +class ShowPatchUpdateStrategy(SwPatchManagerMixin, + sw_update_manager.ShowSwUpdateStrategy): + """Show the details of a patch update strategy for a subcloud.""" + pass + + +class DeletePatchUpdateStrategy(SwPatchManagerMixin, + sw_update_manager.DeleteSwUpdateStrategy): + """Delete patch update strategy from the database.""" + pass + + +class ApplyPatchUpdateStrategy(SwPatchManagerMixin, + sw_update_manager.ApplySwUpdateStrategy): + """Apply a patch update strategy.""" + pass + + +class AbortPatchUpdateStrategy(SwPatchManagerMixin, + sw_update_manager.AbortSwUpdateStrategy): + """Abort a patch update strategy.""" + pass diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/sw_update_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/sw_update_manager.py index 93a3d93..37327c9 100644 --- a/distributedcloud-client/dcmanagerclient/commands/v1/sw_update_manager.py +++ b/distributedcloud-client/dcmanagerclient/commands/v1/sw_update_manager.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Copyright (c) 2017 Wind River Systems, Inc. +# Copyright (c) 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 @@ -22,9 +22,16 @@ from dcmanagerclient.commands.v1 import base from dcmanagerclient import exceptions +# These are the abstract base classes used for sw update managers such as +# - sw-patch-manager +# - fw-update-manager +# +# also handles 'steps' and 'strategies' + def detail_format(sw_update_strategy=None): columns = ( + 'strategy type', 'subcloud apply type', 'max parallel subclouds', 'stop on failure', @@ -35,6 +42,7 @@ def detail_format(sw_update_strategy=None): if sw_update_strategy: data = ( + sw_update_strategy.strategy_type, sw_update_strategy.subcloud_apply_type, sw_update_strategy.max_parallel_subclouds, sw_update_strategy.stop_on_failure, @@ -104,14 +112,18 @@ def detail_strategy_step_format(strategy_step=None): return columns, data -class CreatePatchStrategy(base.DCManagerShowOne): - """Create a patch strategy.""" +class CreateSwUpdateStrategy(base.DCManagerShowOne): + """Create a software update strategy.""" + + def get_sw_update_manager(self): + # This method must be overrridden by the concrete subclass + raise NotImplementedError def _get_format_function(self): return detail_format def get_parser(self, prog_name): - parser = super(CreatePatchStrategy, self).get_parser(prog_name) + parser = super(CreateSwUpdateStrategy, self).get_parser(prog_name) parser.add_argument( '--subcloud-apply-type', @@ -131,20 +143,18 @@ class CreatePatchStrategy(base.DCManagerShowOne): '--stop-on-failure', required=False, action='store_true', - help='Do not patch any additional subclouds after a failure.' + help='Do not update any additional subclouds after a failure.' ) parser.add_argument( 'cloud_name', nargs='?', default=None, - help='Name of a single cloud to patch.' + help='Name of a single cloud to update.' ) - return parser def _get_resources(self, parsed_args): - dcmanager_client = self.app.client_manager.sw_update_manager kwargs = dict() if parsed_args.subcloud_apply_type: kwargs['subcloud-apply-type'] = parsed_args.subcloud_apply_type @@ -155,118 +165,114 @@ class CreatePatchStrategy(base.DCManagerShowOne): kwargs['stop-on-failure'] = 'true' if parsed_args.cloud_name is not None: kwargs['cloud_name'] = parsed_args.cloud_name - return dcmanager_client.sw_update_manager.create_patch_strategy( - **kwargs) + return self.get_sw_update_manager().create_sw_update_strategy(**kwargs) -class ShowPatchStrategy(base.DCManagerShowOne): - """Show the details of a patch strategy for a subcloud.""" +class ShowSwUpdateStrategy(base.DCManagerShowOne): + """Show the details of an software update strategy for a subcloud.""" + + def get_sw_update_manager(self): + # This method must be overrridden by the concrete subclass + raise NotImplementedError def _get_format_function(self): return detail_format - def get_parser(self, prog_name): - parser = super(ShowPatchStrategy, self).get_parser(prog_name) - return parser - def _get_resources(self, parsed_args): - dcmanager_client = self.app.client_manager.sw_update_manager - return dcmanager_client.sw_update_manager.patch_strategy_detail() + return self.get_sw_update_manager().update_sw_strategy_detail() -class DeletePatchStrategy(base.DCManagerShowOne): - """Delete patch strategy from the database.""" +class DeleteSwUpdateStrategy(base.DCManagerShowOne): + """Delete a software update strategy from the database.""" + + def get_sw_update_manager(self): + # This method must be overrridden by the concrete subclass + raise NotImplementedError def _get_format_function(self): return detail_format - def get_parser(self, prog_name): - parser = super(DeletePatchStrategy, self).get_parser(prog_name) - return parser - def _get_resources(self, parsed_args): - dcmanager_client = self.app.client_manager.sw_update_manager try: - return dcmanager_client.sw_update_manager.delete_patch_strategy() + return self.get_sw_update_manager().delete_sw_update_strategy() except Exception as e: print(e) - error_msg = "Unable to delete patch strategy" + error_msg = "Unable to delete sw update strategy" raise exceptions.DCManagerClientException(error_msg) -class ApplyPatchStrategy(base.DCManagerShowOne): - """Apply a patch strategy.""" +class ApplySwUpdateStrategy(base.DCManagerShowOne): + """Apply a software update strategy.""" + + def get_sw_update_manager(self): + # This method must be overrridden by the concrete subclass + raise NotImplementedError def _get_format_function(self): return detail_format - def get_parser(self, prog_name): - parser = super(ApplyPatchStrategy, self).get_parser(prog_name) - return parser - def _get_resources(self, parsed_args): - dcmanager_client = self.app.client_manager.sw_update_manager try: - return dcmanager_client.sw_update_manager.apply_patch_strategy() + return self.get_sw_update_manager().apply_sw_update_strategy() except Exception as e: print(e) - error_msg = "Unable to apply patch strategy" + error_msg = "Unable to apply sw update strategy" raise exceptions.DCManagerClientException(error_msg) -class AbortPatchStrategy(base.DCManagerShowOne): - """Abort a patch strategy.""" +class AbortSwUpdateStrategy(base.DCManagerShowOne): + """Abort a software update strategy.""" + + def get_sw_update_manager(self): + # This method must be overrridden by the concrete subclass + raise NotImplementedError def _get_format_function(self): return detail_format - def get_parser(self, prog_name): - parser = super(AbortPatchStrategy, self).get_parser(prog_name) - return parser - def _get_resources(self, parsed_args): - dcmanager_client = self.app.client_manager.sw_update_manager try: - return dcmanager_client.sw_update_manager.abort_patch_strategy() + return self.get_sw_update_manager().abort_sw_update_strategy() except Exception as e: print(e) - error_msg = "Unable to abort patch strategy" + error_msg = "Unable to abort sw update strategy" raise exceptions.DCManagerClientException(error_msg) -class ListStrategyStep(base.DCManagerLister): +class ListSwUpdateStrategyStep(base.DCManagerLister): """List strategy steps.""" + def get_strategy_step_manager(self): + dcmanager_client = self.app.client_manager.strategy_step_manager + return dcmanager_client.strategy_step_manager + def _get_format_function(self): return strategy_step_format - def get_parser(self, prog_name): - parser = super(ListStrategyStep, self).get_parser(prog_name) - return parser - def _get_resources(self, parsed_args): - dcmanager_client = self.app.client_manager.strategy_step_manager - return dcmanager_client.strategy_step_manager.list_strategy_steps() + return self.get_strategy_step_manager().list_strategy_steps() -class ShowStrategyStep(base.DCManagerShowOne): +class ShowSwUpdateStrategyStep(base.DCManagerShowOne): """Show the details of a strategy step.""" + def get_strategy_step_manager(self): + dcmanager_client = self.app.client_manager.strategy_step_manager + return dcmanager_client.strategy_step_manager + def _get_format_function(self): return detail_strategy_step_format def get_parser(self, prog_name): - parser = super(ShowStrategyStep, self).get_parser(prog_name) + parser = super(ShowSwUpdateStrategyStep, self).get_parser(prog_name) parser.add_argument( 'cloud_name', help='Name of cloud to view the details.' ) - return parser def _get_resources(self, parsed_args): cloud_name = parsed_args.cloud_name - dcmanager_client = self.app.client_manager.strategy_step_manager - return dcmanager_client.strategy_step_manager.strategy_step_detail( + return self.get_strategy_step_manager().strategy_step_detail( cloud_name) diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/sw_upgrade_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/sw_upgrade_manager.py new file mode 100644 index 0000000..f33f490 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/commands/v1/sw_upgrade_manager.py @@ -0,0 +1,59 @@ +# Copyright (c) 2017 Ericsson AB. +# +# 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. +# +# Copyright (c) 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 +# of an applicable Wind River license agreement. +# +from dcmanagerclient.commands.v1 import sw_update_manager + + +class SwUpgradeManagerMixin(object): + """This Mixin provides the update manager used for software upgrades.""" + + def get_sw_update_manager(self): + dcmanager_client = self.app.client_manager.sw_upgrade_manager + return dcmanager_client.sw_upgrade_manager + + +class CreateSwUpgradeStrategy(SwUpgradeManagerMixin, + sw_update_manager.CreateSwUpdateStrategy): + """Create a software upgrade strategy.""" + pass + + +class ShowSwUpgradeStrategy(SwUpgradeManagerMixin, + sw_update_manager.ShowSwUpdateStrategy): + """Show the details of a software upgrade strategy for a subcloud.""" + pass + + +class DeleteSwUpgradeStrategy(SwUpgradeManagerMixin, + sw_update_manager.DeleteSwUpdateStrategy): + """Delete software upgrade strategy from the database.""" + pass + + +class ApplySwUpgradeStrategy(SwUpgradeManagerMixin, + sw_update_manager.ApplySwUpdateStrategy): + """Apply a software upgrade strategy.""" + pass + + +class AbortSwUpgradeStrategy(SwUpgradeManagerMixin, + sw_update_manager.AbortSwUpdateStrategy): + """Abort a software upgrade strategy.""" + pass diff --git a/distributedcloud-client/dcmanagerclient/shell.py b/distributedcloud-client/dcmanagerclient/shell.py index 40a060a..15441c5 100644 --- a/distributedcloud-client/dcmanagerclient/shell.py +++ b/distributedcloud-client/dcmanagerclient/shell.py @@ -37,11 +37,14 @@ from osc_lib.command import command import argparse from dcmanagerclient.commands.v1 import alarm_manager as am +# from dcmanagerclient.commands.v1 import fw_update_manager as fum from dcmanagerclient.commands.v1 import subcloud_deploy_manager as sdm from dcmanagerclient.commands.v1 import subcloud_group_manager as gm from dcmanagerclient.commands.v1 import subcloud_manager as sm +from dcmanagerclient.commands.v1 import sw_patch_manager as spm from dcmanagerclient.commands.v1 import sw_update_manager as sum from dcmanagerclient.commands.v1 import sw_update_options_manager as suom +from dcmanagerclient.commands.v1 import sw_upgrade_manager as supm LOG = logging.getLogger(__name__) @@ -449,9 +452,11 @@ class DCManagerShell(app.App): subcloud_group_manager=self.client, subcloud_deploy_manager=self.client, alarm_manager=self.client, - sw_update_manager=self.client, + fw_update_manager=self.client, + sw_patch_manager=self.client, strategy_step_manager=self.client, - sw_update_options_manager=self.client) + sw_update_options_manager=self.client, + sw_upgrade_manager=self.client) ) self.client_manager = ClientManager() @@ -493,17 +498,31 @@ class DCManagerShell(app.App): 'subcloud-deploy upload': sdm.SubcloudDeployUpload, 'subcloud-deploy show': sdm.SubcloudDeployShow, 'alarm summary': am.ListAlarmSummary, - 'patch-strategy create': sum.CreatePatchStrategy, - 'patch-strategy delete': sum.DeletePatchStrategy, - 'patch-strategy apply': sum.ApplyPatchStrategy, - 'patch-strategy abort': sum.AbortPatchStrategy, - 'patch-strategy show': sum.ShowPatchStrategy, - 'strategy-step list': sum.ListStrategyStep, - 'strategy-step show': sum.ShowStrategyStep, + # 'fw-update-strategy create': fum.CreateFwUpdateStrategy, + # 'fw-update-strategy delete': fum.DeleteFwUpdateStrategy, + # 'fw-update-strategy apply': fum.ApplyFwUpdateStrategy, + # 'fw-update-strategy abort': fum.AbortFwUpdateStrategy, + # 'fw-update-strategy show': fum.ShowFwUpdateStrategy, + 'patch-strategy create': spm.CreatePatchUpdateStrategy, + 'patch-strategy delete': spm.DeletePatchUpdateStrategy, + 'patch-strategy apply': spm.ApplyPatchUpdateStrategy, + 'patch-strategy abort': spm.AbortPatchUpdateStrategy, + 'patch-strategy show': spm.ShowPatchUpdateStrategy, + 'strategy-step list': sum.ListSwUpdateStrategyStep, + 'strategy-step show': sum.ShowSwUpdateStrategyStep, 'patch-strategy-config update': suom.UpdateSwUpdateOptions, 'patch-strategy-config list': suom.ListSwUpdateOptions, 'patch-strategy-config show': suom.ShowSwUpdateOptions, 'patch-strategy-config delete': suom.DeleteSwUpdateOptions, + 'strategy-config update': suom.UpdateSwUpdateOptions, + 'strategy-config list': suom.ListSwUpdateOptions, + 'strategy-config show': suom.ShowSwUpdateOptions, + 'strategy-config delete': suom.DeleteSwUpdateOptions, + 'upgrade-strategy create': supm.CreateSwUpgradeStrategy, + 'upgrade-strategy delete': supm.DeleteSwUpgradeStrategy, + 'upgrade-strategy apply': supm.ApplySwUpgradeStrategy, + 'upgrade-strategy abort': supm.AbortSwUpgradeStrategy, + 'upgrade-strategy show': supm.ShowSwUpgradeStrategy, } diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_step_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_step_manager.py new file mode 100644 index 0000000..084de14 --- /dev/null +++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_step_manager.py @@ -0,0 +1,68 @@ +# Copyright (c) 2017 Ericsson AB. +# +# 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. +# +# Copyright (c) 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 +# of an applicable Wind River license agreement. +# + +import mock + +from oslo_utils import timeutils + +from dcmanagerclient.api.v1.strategy_step_manager import StrategyStep +from dcmanagerclient.commands.v1 import sw_update_manager as cli_cmd +from dcmanagerclient.tests import base + +TEST_CLOUD_ID = 1 +TEST_STAGE = 1 +TEST_STATE = 'initializing' +TEST_DETAILS = 'some details' +TIME_NOW = timeutils.utcnow().isoformat() +TEST_STARTED_AT = TIME_NOW +TEST_FINISHED_AT = TIME_NOW +TEST_CREATED_AT = TIME_NOW +TEST_UPDATED_AT = TIME_NOW + + +class TestCLI(base.BaseCommandTest): + + def setUp(self): + super(TestCLI, self).setUp() + + def test_list_strategy_steps(self): + sample_step = StrategyStep(mock, + TEST_CLOUD_ID, + TEST_STAGE, + TEST_STATE, + TEST_DETAILS, + TEST_STARTED_AT, + TEST_FINISHED_AT, + TEST_CREATED_AT, + TEST_UPDATED_AT) + results = [] + results.append(sample_step) + self.app.client_manager.strategy_step_manager.strategy_step_manager.\ + list_strategy_steps.return_value = results + + actual_call = self.call(cli_cmd.ListSwUpdateStrategyStep) + # ListStrategyStep returns a tuple, want the second field of the tuple + result_steps = actual_call[1] + # Only 1 step + self.assertEqual(1, len(result_steps)) + # The step object is a tuple based on the formatter + for step in result_steps: + self.assertEqual(TEST_CLOUD_ID, step[0])