Merge "Change --patch to patch_id in Patch Orch legacy"
This commit is contained in:
commit
1e63839869
@ -13,15 +13,16 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from oslo_log import log
|
||||
import json
|
||||
|
||||
from nfv_client.openstack import rest_api
|
||||
from nfv_client.openstack import sw_update
|
||||
from oslo_log import log
|
||||
|
||||
from dccommon import consts
|
||||
from dccommon.drivers import base
|
||||
from dccommon import exceptions
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
STRATEGY_NAME_FW_UPDATE = "fw-update"
|
||||
@ -122,6 +123,20 @@ class VimClient(base.DriverBase):
|
||||
"""Create orchestration strategy"""
|
||||
|
||||
url = self.endpoint
|
||||
|
||||
# TODO(nicodemos): Remove once sw-patch is deprecated
|
||||
# Use the REST Api directly to the subcloud to create the strategy for legacy
|
||||
# patch orchestration
|
||||
if strategy_name == STRATEGY_NAME_SW_PATCH:
|
||||
return self._create_strategy_sw_patch(
|
||||
strategy_name,
|
||||
default_instance_action,
|
||||
storage_apply_type,
|
||||
worker_apply_type,
|
||||
max_parallel_worker_hosts,
|
||||
alarm_restrictions,
|
||||
)
|
||||
|
||||
strategy = sw_update.create_strategy(
|
||||
self.token,
|
||||
url,
|
||||
@ -136,7 +151,7 @@ class VimClient(base.DriverBase):
|
||||
username=self.username,
|
||||
user_domain_name=self.user_domain_name,
|
||||
tenant=self.tenant,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
if not strategy:
|
||||
raise Exception("Strategy:(%s) creation failed" % strategy_name)
|
||||
@ -144,6 +159,45 @@ class VimClient(base.DriverBase):
|
||||
LOG.debug("Strategy created: %s" % strategy)
|
||||
return strategy
|
||||
|
||||
# TODO(nicodemos): Delete this method once sw-patch is deprecated
|
||||
def _create_strategy_sw_patch(
|
||||
self,
|
||||
strategy_name,
|
||||
default_instance_action,
|
||||
storage_apply_type,
|
||||
worker_apply_type,
|
||||
max_parallel_worker_hosts,
|
||||
alarm_restrictions,
|
||||
):
|
||||
api_cmd = self.endpoint + "/api/orchestration/%s/strategy" % strategy_name
|
||||
|
||||
api_cmd_headers = dict()
|
||||
api_cmd_headers["Content-Type"] = "application/json"
|
||||
api_cmd_headers["X-User"] = self.username
|
||||
api_cmd_headers["X-Tenant"] = self.tenant
|
||||
api_cmd_headers["X-User-Domain-Name"] = self.user_domain_name
|
||||
api_cmd_headers["X-Auth-Token"] = self.token
|
||||
|
||||
api_cmd_payload = dict()
|
||||
api_cmd_payload["controller-apply-type"] = APPLY_TYPE_SERIAL
|
||||
api_cmd_payload["swift-apply-type"] = APPLY_TYPE_IGNORE
|
||||
api_cmd_payload["default-instance-action"] = default_instance_action
|
||||
api_cmd_payload["storage-apply-type"] = storage_apply_type
|
||||
api_cmd_payload["worker-apply-type"] = worker_apply_type
|
||||
if max_parallel_worker_hosts is not None:
|
||||
api_cmd_payload["max-parallel-worker-hosts"] = max_parallel_worker_hosts
|
||||
api_cmd_payload["alarm-restrictions"] = alarm_restrictions
|
||||
|
||||
response = rest_api.request(
|
||||
self.token, "POST", api_cmd, api_cmd_headers, json.dumps(api_cmd_payload)
|
||||
)
|
||||
|
||||
# Check if the response is valid and update response value
|
||||
if response.get("strategy"):
|
||||
response["strategy"]["build-phase"]["response"] = "success"
|
||||
|
||||
return sw_update._get_strategy_object_from_response(response)
|
||||
|
||||
def get_strategy(self, strategy_name, raise_error_if_missing=True):
|
||||
"""Get the current orchestration strategy"""
|
||||
|
||||
|
@ -168,14 +168,19 @@ class SwUpdateStrategyController(object):
|
||||
]:
|
||||
pecan.abort(400, _("subcloud-apply-type invalid"))
|
||||
|
||||
patch_file = payload.get("patch")
|
||||
if strategy_type == consts.SW_UPDATE_TYPE_PATCH:
|
||||
if not patch_file:
|
||||
message = (
|
||||
f"patch parameter is required for {strategy_type} strategy."
|
||||
)
|
||||
pecan.abort(400, _(message))
|
||||
elif not os.path.isfile(patch_file):
|
||||
patch_id = payload.get("patch_id")
|
||||
if strategy_type == consts.SW_UPDATE_TYPE_PATCH and not patch_id:
|
||||
message = (
|
||||
f"patch_id parameter is required for {strategy_type} strategy."
|
||||
)
|
||||
pecan.abort(400, _(message))
|
||||
elif patch_id:
|
||||
patch_file = (
|
||||
f"{consts.PATCH_VAULT_DIR}/{consts.PATCHING_SW_VERSION}/"
|
||||
f"{patch_id}.patch"
|
||||
)
|
||||
|
||||
if not os.path.isfile(patch_file):
|
||||
message = f"Patch file {patch_file} is missing."
|
||||
pecan.abort(400, _(message))
|
||||
|
||||
|
@ -343,7 +343,10 @@ EXTRA_ARGS_FORCE = "force"
|
||||
|
||||
# extra_args for patching
|
||||
EXTRA_ARGS_UPLOAD_ONLY = "upload-only"
|
||||
EXTRA_ARGS_PATCH = "patch"
|
||||
EXTRA_ARGS_PATCH_ID = "patch_id"
|
||||
|
||||
# sw_version supported for patching
|
||||
PATCHING_SW_VERSION = "22.12"
|
||||
|
||||
# extra_args for software
|
||||
EXTRA_ARGS_RELEASE_ID = "release_id"
|
||||
|
@ -4,7 +4,6 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
from dcmanager.common import consts
|
||||
@ -34,11 +33,6 @@ class UpdatingPatchesState(BaseState):
|
||||
def upload_patch(self, patch_file, strategy_step):
|
||||
"""Upload a patch file to the subcloud"""
|
||||
|
||||
if not os.path.isfile(patch_file):
|
||||
message = f"Patch file {patch_file} is missing"
|
||||
self.error_log(strategy_step, message)
|
||||
raise Exception(message)
|
||||
|
||||
self.info_log(
|
||||
strategy_step,
|
||||
f"Patch {patch_file} will be uploaded to subcloud",
|
||||
@ -51,9 +45,6 @@ class UpdatingPatchesState(BaseState):
|
||||
def perform_state_action(self, strategy_step):
|
||||
"""Update patches in this subcloud"""
|
||||
self.info_log(strategy_step, "Updating patches")
|
||||
extra_args = utils.get_sw_update_strategy_extra_args(self.context)
|
||||
upload_only = extra_args.get(consts.EXTRA_ARGS_UPLOAD_ONLY)
|
||||
patch_file = extra_args.get(consts.EXTRA_ARGS_PATCH)
|
||||
|
||||
# Retrieve all subcloud patches
|
||||
try:
|
||||
@ -65,8 +56,12 @@ class UpdatingPatchesState(BaseState):
|
||||
|
||||
subcloud_patch_ids = subcloud_patches.keys()
|
||||
|
||||
patch = os.path.basename(patch_file)
|
||||
patch_id = os.path.splitext(patch)[0]
|
||||
extra_args = utils.get_sw_update_strategy_extra_args(self.context)
|
||||
patch_id = extra_args.get(consts.EXTRA_ARGS_PATCH_ID)
|
||||
patch_file = (
|
||||
f"{consts.PATCH_VAULT_DIR}/{consts.PATCHING_SW_VERSION}/"
|
||||
f"{patch_id}.patch"
|
||||
)
|
||||
|
||||
if patch_id in subcloud_patch_ids:
|
||||
message = f"Patch {patch_id} is already present in the subcloud."
|
||||
@ -74,6 +69,8 @@ class UpdatingPatchesState(BaseState):
|
||||
else:
|
||||
self.upload_patch(patch_file, strategy_step)
|
||||
|
||||
upload_only = extra_args.get(consts.EXTRA_ARGS_UPLOAD_ONLY)
|
||||
|
||||
if upload_only:
|
||||
self.info_log(
|
||||
strategy_step,
|
||||
@ -85,7 +82,7 @@ class UpdatingPatchesState(BaseState):
|
||||
# Apply the patch to the subcloud
|
||||
self.info_log(
|
||||
strategy_step,
|
||||
f"Patch {patch_file} will be applied to subcloud",
|
||||
f"Patch {patch_id} will be applied to subcloud",
|
||||
)
|
||||
self.get_patching_client(self.region_name).apply([patch_id])
|
||||
|
||||
|
@ -38,5 +38,5 @@ class PatchStrategyValidator(StrategyValidationBase):
|
||||
upload_only_bool = payload.get(consts.EXTRA_ARGS_UPLOAD_ONLY) == "true"
|
||||
return {
|
||||
consts.EXTRA_ARGS_UPLOAD_ONLY: upload_only_bool,
|
||||
consts.EXTRA_ARGS_PATCH: payload.get(consts.EXTRA_ARGS_PATCH),
|
||||
consts.EXTRA_ARGS_PATCH_ID: payload.get(consts.EXTRA_ARGS_PATCH_ID),
|
||||
}
|
||||
|
@ -14,12 +14,12 @@ from dcmanager.tests.unit.common import fake_strategy
|
||||
from dcmanager.tests.unit.orchestrator.states.patch.test_base import TestPatchState
|
||||
|
||||
SUBCLOUD_NO_USM_PATCHES = {
|
||||
"DC.3": {
|
||||
"stx-9.1": {
|
||||
"sw_version": "sxt-9.0",
|
||||
"repostate": "Available",
|
||||
"patchstate": "Partial-Remove",
|
||||
},
|
||||
"DC.6": {
|
||||
"stx-9.2": {
|
||||
"sw_version": "sxt-9.0",
|
||||
"repostate": "Applied",
|
||||
"patchstate": "Partial-Apply",
|
||||
@ -27,18 +27,20 @@ SUBCLOUD_NO_USM_PATCHES = {
|
||||
}
|
||||
|
||||
SUBCLOUD_USM_PATCHES = {
|
||||
"usm": {
|
||||
"sw_version": "stx8",
|
||||
"stx-9.1": {
|
||||
"sw_version": "stx-9.0",
|
||||
"repostate": "Available",
|
||||
"patchstate": "Available",
|
||||
},
|
||||
"DC.3": {
|
||||
"sw_version": "20.12",
|
||||
"stx-usm-9.2": {
|
||||
"sw_version": "stx-9.0",
|
||||
"repostate": "Available",
|
||||
"patchstate": "Partial-Remove",
|
||||
},
|
||||
}
|
||||
|
||||
DC_VAULT_PATCH_DIR = "/opt/dc-vault/patches/22.12/"
|
||||
|
||||
|
||||
@mock.patch(
|
||||
"dcmanager.orchestrator.states.patch.updating_patches.DEFAULT_MAX_QUERIES", 3
|
||||
@ -67,44 +69,46 @@ class TestUpdatingPatchesStage(TestPatchState):
|
||||
self.patching_client.apply = mock.MagicMock()
|
||||
self.patching_client.query_hosts = mock.MagicMock()
|
||||
|
||||
def _create_fake_strategy(self, upload_only=False, patch_file=None):
|
||||
def _create_fake_strategy(self, upload_only=False, patch_id=None):
|
||||
extra_args = {
|
||||
consts.EXTRA_ARGS_UPLOAD_ONLY: upload_only,
|
||||
consts.EXTRA_ARGS_PATCH: patch_file,
|
||||
consts.EXTRA_ARGS_PATCH_ID: patch_id,
|
||||
}
|
||||
return fake_strategy.create_fake_strategy(
|
||||
self.ctx, self.DEFAULT_STRATEGY_TYPE, extra_args=extra_args
|
||||
)
|
||||
|
||||
@mock.patch.object(os_path, "isfile")
|
||||
def test_update_subcloud_patches_patch_file_success(self, mock_os_path_isfile):
|
||||
"""Test update_patches where the API call succeeds patch parameter."""
|
||||
def test_update_subcloud_patches_patch_id_success(self, mock_os_path_isfile):
|
||||
"""Test update_patches where the API call succeeds with patch_id parameter."""
|
||||
|
||||
mock_os_path_isfile.return_value = True
|
||||
|
||||
self.patching_client.query.side_effect = [SUBCLOUD_NO_USM_PATCHES]
|
||||
|
||||
self._create_fake_strategy(patch_file="usm.patch")
|
||||
self._create_fake_strategy(patch_id="stx-usm-9.2")
|
||||
|
||||
# invoke the strategy state operation on the orch thread
|
||||
self.worker.perform_state_action(self.strategy_step)
|
||||
|
||||
self.patching_client.upload.assert_called_with(["usm.patch"])
|
||||
self.patching_client.upload.assert_called_with(
|
||||
[DC_VAULT_PATCH_DIR + "stx-usm-9.2.patch"]
|
||||
)
|
||||
|
||||
call_args, _ = self.patching_client.apply.call_args_list[0]
|
||||
self.assertItemsEqual(["usm"], call_args[0])
|
||||
self.assertItemsEqual(["stx-usm-9.2"], call_args[0])
|
||||
|
||||
# On success, the state should transition to the next state
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id, self.success_state)
|
||||
|
||||
self.assert_step_details(self.strategy_step.subcloud_id, "")
|
||||
|
||||
def test_update_subcloud_patches_patch_file_no_upload(self):
|
||||
"""Test update_patches where the API call patch parameter is not uploaded."""
|
||||
def test_update_subcloud_patches_patch_id_no_upload(self):
|
||||
"""Test update_patches where the API using patch_id parameter isn't uploaded."""
|
||||
|
||||
self.patching_client.query.side_effect = [SUBCLOUD_USM_PATCHES]
|
||||
|
||||
self._create_fake_strategy(patch_file="usm.patch")
|
||||
self._create_fake_strategy(patch_id="stx-usm-9.2")
|
||||
|
||||
# invoke the strategy state operation on the orch thread
|
||||
self.worker.perform_state_action(self.strategy_step)
|
||||
@ -112,7 +116,7 @@ class TestUpdatingPatchesStage(TestPatchState):
|
||||
self.patching_client.upload.assert_not_called()
|
||||
|
||||
call_args, _ = self.patching_client.apply.call_args_list[0]
|
||||
self.assertItemsEqual(["usm"], call_args[0])
|
||||
self.assertItemsEqual(["stx-usm-9.2"], call_args[0])
|
||||
|
||||
# On success, the state should transition to the next state
|
||||
self.assert_step_updated(self.strategy_step.subcloud_id, self.success_state)
|
||||
@ -120,21 +124,23 @@ class TestUpdatingPatchesStage(TestPatchState):
|
||||
self.assert_step_details(self.strategy_step.subcloud_id, "")
|
||||
|
||||
@mock.patch.object(os_path, "isfile")
|
||||
def test_update_subcloud_patches_patch_file_upload_only_success(
|
||||
def test_update_subcloud_patches_patch_id_upload_only_success(
|
||||
self, mock_os_path_isfile
|
||||
):
|
||||
"""Test update_patches where the API call succeeds with patch/upload only."""
|
||||
"""Test update_patches where the API call succeeds with patch_id/upload only."""
|
||||
|
||||
mock_os_path_isfile.return_value = True
|
||||
|
||||
self.patching_client.query.side_effect = [SUBCLOUD_NO_USM_PATCHES]
|
||||
|
||||
self._create_fake_strategy(upload_only=True, patch_file="usm.patch")
|
||||
self._create_fake_strategy(upload_only=True, patch_id="stx-usm-9.2")
|
||||
|
||||
# invoke the strategy state operation on the orch thread
|
||||
self.worker.perform_state_action(self.strategy_step)
|
||||
|
||||
self.patching_client.upload.assert_called_with(["usm.patch"])
|
||||
self.patching_client.upload.assert_called_with(
|
||||
[DC_VAULT_PATCH_DIR + "stx-usm-9.2.patch"]
|
||||
)
|
||||
self.patching_client.apply.assert_not_called()
|
||||
|
||||
self.assert_step_details(self.strategy_step.subcloud_id, "")
|
||||
@ -149,12 +155,12 @@ class TestUpdatingPatchesStage(TestPatchState):
|
||||
def test_updating_subcloud_patches_fails_when_stopped(
|
||||
self, mock_os_path_isfile, mock_base_stopped
|
||||
):
|
||||
"""Test finish strategy fails when stopped"""
|
||||
"""Test update_patches state fails when stopped"""
|
||||
mock_os_path_isfile.return_value = True
|
||||
|
||||
self.patching_client.query.side_effect = [SUBCLOUD_NO_USM_PATCHES]
|
||||
|
||||
self._create_fake_strategy(upload_only=True, patch_file="usm.patch")
|
||||
self._create_fake_strategy(upload_only=True, patch_id="stx-usm-9.2")
|
||||
|
||||
mock_base_stopped.return_value = True
|
||||
|
||||
|
@ -39,7 +39,10 @@ class TestPatchValidator(
|
||||
return self.validator
|
||||
|
||||
def _get_build_extra_args_payload(self):
|
||||
return {consts.EXTRA_ARGS_UPLOAD_ONLY: True, consts.EXTRA_ARGS_PATCH: None}
|
||||
return {
|
||||
consts.EXTRA_ARGS_UPLOAD_ONLY: True,
|
||||
consts.EXTRA_ARGS_PATCH_ID: "stx-9.1",
|
||||
}
|
||||
|
||||
def _get_expected_extra_args(self):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user