USM: Change deploy precheck and start to accept arbitrary arguments.

This change adds arbitrary parameters, to reduce or avoid the need to
patchback when new features are added.
The snapshot parameter was removed to use this new change with -o
snapshot=true.
This commit also add a sanitize function to allow only letters, numbers,
space, - and _.
Usage: software deploy start/precheck <major release> -o snapshot=true
--options test=test

Test Plan:
PASS: Build-pkgs, Build-image, Install/Bootstrap/Unlock
PASS: Check precheck script receives the parameters
(by injecting logs manually)
PASS: Check deploy start receives the parameters by injecting
manual logs.
PASS: Check software.json stores the parameters under options field.
PASS: Deploy start blocked with reserved key in options.
PASS: Check activate-rollback is using deploy options for snapshot.
PASS: Major release deployment stx10 -> stx11 in sx.

Story: 2011357
Task: 52264

Change-Id: I53b8863ceeec74c45831d959fe6cea00990cc156
Signed-off-by: Luis Eduardo Bonatti <luizeduardo.bonatti@windriver.com>
This commit is contained in:
Luis Eduardo Bonatti
2025-05-22 17:02:32 -03:00
parent a8b3c98117
commit fdbb073719
11 changed files with 119 additions and 52 deletions

View File

@@ -433,6 +433,7 @@ confidence=HIGH,
# -Refactoring- # -Refactoring-
# R0205 useless-object-inheritance # R0205 useless-object-inheritance
# R0402 consider-using-from-import # R0402 consider-using-from-import
# R0801 Similar lines in x files
# R0903 too-few-public-methods # R0903 too-few-public-methods
# R0911 too-many-return-statements # R0911 too-many-return-statements
# R0912 too-many-branches # R0912 too-many-branches
@@ -453,7 +454,7 @@ confidence=HIGH,
# W1514 unspecified-encoding # W1514 unspecified-encoding
# W3101 missing-timeout # W3101 missing-timeout
disable= C0103,C0114,C0115,C0116,C0209,C0301,C0302,C0411, disable= C0103,C0114,C0115,C0116,C0209,C0301,C0302,C0411,
C0415,R0205,R0402,R0903,R0911,R0912,R0914,R0915, C0415,R0205,R0402,R0801,R0903,R0911,R0912,R0914,R0915,
R1702,R1705,R1714,R1722,R1724,R1725,R1732,R1735, R1702,R1705,R1714,R1722,R1724,R1725,R1732,R1735,
W0602,W0603,W0718,W1514,W3101, W0602,W0603,W0718,W1514,W3101,

View File

@@ -30,9 +30,8 @@ class DeployManager(base.Manager):
body = {} body = {}
if args.force: if args.force:
body["force"] = "true" body["force"] = "true"
if args.snapshot: if args.options:
body["snapshot"] = "true" body["options"] = args.options
if args.region_name: if args.region_name:
body["region_name"] = args.region_name body["region_name"] = args.region_name
@@ -51,8 +50,8 @@ class DeployManager(base.Manager):
body = {} body = {}
if args.force: if args.force:
body["force"] = "true" body["force"] = "true"
if args.snapshot: if args.options:
body["snapshot"] = "true" body["options"] = args.options
return self._post(path, body=body) return self._post(path, body=body)

View File

@@ -63,11 +63,11 @@ def do_host_list(cc, args):
default=None, default=None,
required=False, required=False,
help='Run precheck against a subcloud') help='Run precheck against a subcloud')
@utils.arg('-s', @utils.arg('-o',
'--snapshot', '--options',
action='store_true', action='append',
required=False, required=False,
help='Execute LVM snapshot feature specific health checks (AIO-SX only)') help='Additional parameters in key=value format.')
def do_precheck(cc, args): def do_precheck(cc, args):
"""Verify whether prerequisites for installing the software deployment are satisfied""" """Verify whether prerequisites for installing the software deployment are satisfied"""
resp, data = cc.deploy.precheck(args) resp, data = cc.deploy.precheck(args)
@@ -91,11 +91,11 @@ def do_precheck(cc, args):
action='store_true', action='store_true',
required=False, required=False,
help='Allow bypassing non-critical checks') help='Allow bypassing non-critical checks')
@utils.arg('-s', @utils.arg('-o',
'--snapshot', '--options',
action='store_true', action='append',
required=False, required=False,
help='Use LVM snapshots for faster rollback/recovery (AIO-SX only)') help='Additional parameters in key=value format.')
def do_start(cc, args): def do_start(cc, args):
"""Start the software deployment""" """Start the software deployment"""
resp, data = cc.deploy.start(args) resp, data = cc.deploy.start(args)

View File

@@ -257,6 +257,11 @@ class UpgradeHealthCheck(HealthCheck):
return success, msg return success, msg
return success, None return success, None
def _check_snapshot_option(self):
options = json.loads(self._config.get("options", {}))
snapshot = options.get("snapshot", False)
return upgrade_utils.to_bool(snapshot)
def run_health_check(self): def run_health_check(self):
"""Run specific upgrade health checks""" """Run specific upgrade health checks"""
@@ -337,7 +342,7 @@ class UpgradeHealthCheck(HealthCheck):
health_ok = health_ok and success health_ok = health_ok and success
# check for LVM snapshot health checks # check for LVM snapshot health checks
use_lvm_snapshot = self._config.get("snapshot", False) use_lvm_snapshot = self._check_snapshot_option()
if use_lvm_snapshot: if use_lvm_snapshot:
success = self._check_system_simplex() success = self._check_system_simplex()
output += '(LVM snapshots) System is AIO-SX: [%s]\n' \ output += '(LVM snapshots) System is AIO-SX: [%s]\n' \
@@ -456,9 +461,8 @@ def parse_config(args=None):
parser.add_argument("--patch", parser.add_argument("--patch",
help="Set precheck to run against a patch release", help="Set precheck to run against a patch release",
action="store_true") action="store_true")
parser.add_argument("--snapshot", parser.add_argument("--options",
help="Execute LVM snapshot feature specific health checks (AIO-SX only)", help="Additional parameters in key=value format.")
action="store_true")
parser.add_argument("--releases", parser.add_argument("--releases",
help="Releases", help="Releases",
default="[]") default="[]")

View File

@@ -13,6 +13,7 @@
# 5. perform data migration # 5. perform data migration
# #
import json
import logging import logging
import os import os
import shutil import shutil
@@ -34,7 +35,7 @@ class DeployStart:
DEPLOY_STATE_START_FAILED = "start-failed" DEPLOY_STATE_START_FAILED = "start-failed"
def __init__(self, from_version, to_version, k8s_version, postgres_port, def __init__(self, from_version, to_version, k8s_version, postgres_port,
feed_ostree_repo_dir, commit_id=None, ignore_errors=False): feed_ostree_repo_dir, commit_id=None, ignore_errors=False, options=None):
self._from_version = from_version self._from_version = from_version
self._to_version = to_version self._to_version = to_version
self._k8s_version = k8s_version.lstrip("v") self._k8s_version = k8s_version.lstrip("v")
@@ -43,6 +44,7 @@ class DeployStart:
self._feed_ostree_repo_url = f"file://{feed_ostree_repo_dir}" self._feed_ostree_repo_url = f"file://{feed_ostree_repo_dir}"
self._commit_id = commit_id self._commit_id = commit_id
self._ignore_errors = ignore_errors self._ignore_errors = ignore_errors
self._options = json.loads(options) if options else {}
def _update_deploy_state(self, state): def _update_deploy_state(self, state):
try: try:
@@ -167,8 +169,8 @@ class DeployStart:
LOG.info("Cleanup complete") LOG.info("Cleanup complete")
def _take_lvm_snapshots(self): def _take_lvm_snapshots(self):
deployment = upgrade_utils.get_deployment_data() snapshot = upgrade_utils.to_bool(self._options.get("snapshot"))
if deployment.get("snapshot") is True: if snapshot is True:
LOG.info("LVM snapshot option enabled, proceeding to take snapshots...") LOG.info("LVM snapshot option enabled, proceeding to take snapshots...")
script_path = os.path.join(self.SCRIPT_DIR, "manage-lvm-snapshots") script_path = os.path.join(self.SCRIPT_DIR, "manage-lvm-snapshots")
cmd = [script_path, "--create"] cmd = [script_path, "--create"]
@@ -208,6 +210,7 @@ if __name__ == "__main__":
postgres_port = None postgres_port = None
feed_ostree_repo_dir = None feed_ostree_repo_dir = None
commit_id = None commit_id = None
options = None
for arg in range(1, len(sys.argv)): for arg in range(1, len(sys.argv)):
if arg == 1: if arg == 1:
from_version = sys.argv[arg] from_version = sys.argv[arg]
@@ -221,16 +224,18 @@ if __name__ == "__main__":
feed_ostree_repo_dir = sys.argv[arg] feed_ostree_repo_dir = sys.argv[arg]
elif arg == 6: elif arg == 6:
commit_id = sys.argv[arg] commit_id = sys.argv[arg]
elif arg == 7:
options = sys.argv[arg]
ignore_errors = os.environ.get("IGNORE_ERRORS", False) ignore_errors = os.environ.get("IGNORE_ERRORS", False)
if any(x is None for x in [from_version, to_version, k8s_version, postgres_port, feed_ostree_repo_dir]): if any(x is None for x in [from_version, to_version, k8s_version, postgres_port, feed_ostree_repo_dir]):
usage_msg = (f"usage: {sys.argv[0]} <from_version> <to_version> <k8s_version> " usage_msg = (f"usage: {sys.argv[0]} <from_version> <to_version> <k8s_version> "
f"<postgresql_port> <feed_ostree_repo_dir> [commit_id]") f"<postgresql_port> <feed_ostree_repo_dir> [commit_id] <options>")
print(usage_msg) print(usage_msg)
LOG.info(usage_msg) LOG.info(usage_msg)
sys.exit(1) sys.exit(1)
deploy_start = DeployStart(from_version, to_version, k8s_version, postgres_port, feed_ostree_repo_dir, deploy_start = DeployStart(from_version, to_version, k8s_version, postgres_port, feed_ostree_repo_dir,
commit_id=commit_id, ignore_errors=ignore_errors) commit_id=commit_id, ignore_errors=ignore_errors, options=options)
sys.exit(deploy_start.run()) sys.exit(deploy_start.run())

View File

@@ -280,3 +280,11 @@ def get_deployment_data():
with open("/opt/software/software.json", "r") as fp: with open("/opt/software/software.json", "r") as fp:
deployment = json.loads(fp.read()) deployment = json.loads(fp.read())
return deployment.get("deploy")[0] return deployment.get("deploy")[0]
def to_bool(value):
if isinstance(value, bool):
return value
if isinstance(value, str):
return value.lower() == 'true'
return False

View File

@@ -72,26 +72,24 @@ class DeployController(RestController):
return result return result
@expose(method='POST', template='json') @expose(method='POST', template='json')
def precheck(self, release, force=None, region_name=None, snapshot=None): def precheck(self, release, force=None, region_name=None, **kwargs):
_force = force is not None _force = force is not None
_snapshot = snapshot is not None
reload_release_data() reload_release_data()
result = sc.software_deploy_precheck_api(release, _force, region_name, _snapshot) result = sc.software_deploy_precheck_api(release, _force, region_name, **kwargs)
if result and 'error' in result and result["error"] != "": if result and 'error' in result and result["error"] != "":
response.status = 406 response.status = 406
return result return result
@expose(method='POST', template='json') @expose(method='POST', template='json')
def start(self, release, force=None, snapshot=None): def start(self, release, force=None, **kwargs):
reload_release_data() reload_release_data()
_force = force is not None _force = force is not None
_snapshot = snapshot is not None
if sc.any_patch_host_installing(): if sc.any_patch_host_installing():
raise SoftwareServiceError(error="Rejected: One or more nodes are installing a release.") raise SoftwareServiceError(error="Rejected: One or more nodes are installing a release.")
result = sc.software_deploy_start_api(release, _force, _snapshot) result = sc.software_deploy_start_api(release, _force, **kwargs)
if result and 'error' in result and result["error"] != "": if result and 'error' in result and result["error"] != "":
response.status = 406 response.status = 406

View File

@@ -238,3 +238,10 @@ LOG_DEFAULT_FORMAT = ('%(asctime)s.%(msecs)03d USM - %(exec)s [%(process)s:%(thr
SOFTWARE_API_SUPPRESS_PATTERNS = [ SOFTWARE_API_SUPPRESS_PATTERNS = [
r"GET /v1/deploy/software_upgrade", r"GET /v1/deploy/software_upgrade",
] ]
# Reserved_words
RESERVED_WORDS_SET = {"OS_AUTH_TOKEN", "OS_AUTH_TYPE", "OS_AUTH_URL", "OS_ENDPOINT_TYPE", "OS_IDENTITY_API_VERSION",
"OS_INTERFACE", "OS_PASSWORD", "OS_PROJECT_DOMAIN_ID", "OS_PROJECT_DOMAIN_NAME", "OS_PROJECT_ID",
"OS_PROJECT_NAME", "OS_REGION_NAME", "OS_SERVICE_TOKEN", "OS_USERNAME", "OS_USER_DOMAIN_NAME",
"OS_SERVICE_TYPE", "OS_TENANT_ID", "OS_TENANT_NAME", "OS_USER_DOMAIN_ID", "SYSTEM_API_VERSION",
"SYSTEM_URL"}

View File

@@ -91,6 +91,7 @@ from software.software_functions import is_deployment_in_progress
from software.software_functions import get_release_from_patch from software.software_functions import get_release_from_patch
from software.software_functions import clean_up_deployment_data from software.software_functions import clean_up_deployment_data
from software.software_functions import run_remove_temporary_data_script from software.software_functions import run_remove_temporary_data_script
from software.software_functions import to_bool
from software.release_state import ReleaseState from software.release_state import ReleaseState
from software.utilities.deploy_set_failed import start_set_fail from software.utilities.deploy_set_failed import start_set_fail
from software.deploy_host_state import DeployHostState from software.deploy_host_state import DeployHostState
@@ -2674,6 +2675,39 @@ class PatchController(PatchService):
finally: finally:
self.socket_lock.release() self.socket_lock.release()
def _sanitize_extra_options(self, value):
"""
Make sure value have only allowed characters.
"""
# Only letters, numbers, space, -, and _ are allowed.
if not re.match(r'^[\w\s\-]+$', value):
msg_error = f"Invalid value: '{value}'."
raise SoftwareServiceError(msg_error)
return value
def _parse_and_sanitize_extra_options(self, options_list):
"""
Validate, sanitize and convert a 'key=value' to dictionary.
"""
for item in options_list:
if item.count('=') != 1:
msg_error = f"Invalid format: '{item}'. Expected format is key=value"
raise SoftwareServiceError(msg_error)
options = {}
for item in options_list:
key, value = item.split('=', 1)
key = self._sanitize_extra_options(key.strip())
value = self._sanitize_extra_options(value.strip())
if key in constants.RESERVED_WORDS_SET:
msg_error = f"{key} is a reserved word and can't be used."
raise SoftwareServiceError(msg_error)
options[key] = value
return options
def _release_basic_checks(self, deployment): def _release_basic_checks(self, deployment):
""" """
Does basic sanity checks on the release data Does basic sanity checks on the release data
@@ -2696,7 +2730,7 @@ class PatchController(PatchService):
def _deploy_precheck(self, release_version: str, force: bool = False, def _deploy_precheck(self, release_version: str, force: bool = False,
region_name: typing.Optional[str] = None, patch: bool = False, region_name: typing.Optional[str] = None, patch: bool = False,
snapshot: bool = False) -> dict: **kwargs) -> dict:
""" """
Verify if system satisfy the requisites to upgrade to a specified deployment. Verify if system satisfy the requisites to upgrade to a specified deployment.
:param release_version: full release name, e.g. starlingx-MM.mm.pp :param release_version: full release name, e.g. starlingx-MM.mm.pp
@@ -2777,13 +2811,12 @@ class PatchController(PatchService):
"--project_domain_name=%s" % project_domain_name, "--project_domain_name=%s" % project_domain_name,
"--region_name=%s" % region_name, "--region_name=%s" % region_name,
"--releases=%s" % json.dumps(releases), "--releases=%s" % json.dumps(releases),
"--options=%s" % json.dumps(kwargs.get("options", {})),
"--deploy_in_progress=%s" % json.dumps(deploy_in_progress)] "--deploy_in_progress=%s" % json.dumps(deploy_in_progress)]
if force: if force:
cmd.append("--force") cmd.append("--force")
if patch: if patch:
cmd.append("--patch") cmd.append("--patch")
if snapshot:
cmd.append("--snapshot")
# Call precheck from the deployment files # Call precheck from the deployment files
precheck_return = subprocess.run( precheck_return = subprocess.run(
@@ -2805,7 +2838,7 @@ class PatchController(PatchService):
return dict(info=msg_info, warning=msg_warning, error=msg_error, system_healthy=system_healthy) return dict(info=msg_info, warning=msg_warning, error=msg_error, system_healthy=system_healthy)
def software_deploy_precheck_api(self, deployment: str, force: bool = False, region_name=None, def software_deploy_precheck_api(self, deployment: str, force: bool = False, region_name=None,
snapshot: bool = False) -> dict: **kwargs) -> dict:
""" """
Verify if system satisfy the requisites to upgrade to a specified deployment. Verify if system satisfy the requisites to upgrade to a specified deployment.
:param deployment: full release name, e.g. starlingx-MM.mm.pp :param deployment: full release name, e.g. starlingx-MM.mm.pp
@@ -2821,8 +2854,9 @@ class PatchController(PatchService):
if not is_patch and socket.gethostname() != constants.CONTROLLER_0_HOSTNAME: if not is_patch and socket.gethostname() != constants.CONTROLLER_0_HOSTNAME:
raise SoftwareServiceError(f"Deploy precheck for major releases needs to be executed in" raise SoftwareServiceError(f"Deploy precheck for major releases needs to be executed in"
f" {constants.CONTROLLER_0_HOSTNAME} host.") f" {constants.CONTROLLER_0_HOSTNAME} host.")
if kwargs.get("options"):
ret = self._deploy_precheck(release_version, force, region_name, is_patch, snapshot) kwargs["options"] = self._parse_and_sanitize_extra_options(kwargs.get("options"))
ret = self._deploy_precheck(release_version, force, region_name, is_patch, **kwargs)
if ret: if ret:
if ret.get("system_healthy") is None: if ret.get("system_healthy") is None:
ret["error"] = "Fail to perform deploy precheck. Internal error has occurred.\n" + \ ret["error"] = "Fail to perform deploy precheck. Internal error has occurred.\n" + \
@@ -2832,7 +2866,7 @@ class PatchController(PatchService):
"deploying %s\n" % deployment + ret.get("info") "deploying %s\n" % deployment + ret.get("info")
return ret return ret
def _deploy_upgrade_start(self, to_release, commit_id): def _deploy_upgrade_start(self, to_release, commit_id, **kwargs):
LOG.info("start deploy upgrade to %s from %s" % (to_release, SW_VERSION)) LOG.info("start deploy upgrade to %s from %s" % (to_release, SW_VERSION))
deploy_script_name = constants.DEPLOY_START_SCRIPT deploy_script_name = constants.DEPLOY_START_SCRIPT
cmd_path = utils.get_software_deploy_script(to_release, deploy_script_name) cmd_path = utils.get_software_deploy_script(to_release, deploy_script_name)
@@ -2853,6 +2887,7 @@ class PatchController(PatchService):
feed] feed]
upgrade_start_cmd.append(commit_id if commit_id is not None else 0) upgrade_start_cmd.append(commit_id if commit_id is not None else 0)
upgrade_start_cmd.append(json.dumps(kwargs.get("options")) if kwargs.get("options") is not None else "")
# pass in keystone auth through environment variables # pass in keystone auth through environment variables
# OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_PROJECT_NAME, OS_USER_DOMAIN_NAME, # OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_PROJECT_NAME, OS_USER_DOMAIN_NAME,
# OS_PROJECT_DOMAIN_NAME, OS_REGION_NAME are in env variables. # OS_PROJECT_DOMAIN_NAME, OS_REGION_NAME are in env variables.
@@ -2869,7 +2904,7 @@ class PatchController(PatchService):
try: try:
LOG.info("starting subprocess %s" % ' '.join(upgrade_start_cmd)) LOG.info("starting subprocess %s" % ' '.join(upgrade_start_cmd))
subprocess.Popen(' '.join(upgrade_start_cmd), start_new_session=True, shell=True, env=env) subprocess.Popen(upgrade_start_cmd, start_new_session=True, shell=False, env=env)
LOG.info("subprocess started") LOG.info("subprocess started")
return True return True
except subprocess.SubprocessError as e: except subprocess.SubprocessError as e:
@@ -2969,7 +3004,7 @@ class PatchController(PatchService):
msg = "Failed to delete patch script %s. Reason: %s" % (script_path, e) msg = "Failed to delete patch script %s. Reason: %s" % (script_path, e)
LOG.error(msg) LOG.error(msg)
def install_releases_thread(self, deployment_list, feed_repo, upgrade=False): def install_releases_thread(self, deployment_list, feed_repo, upgrade=False, **kwargs):
""" """
In a separated thread. In a separated thread.
Install the debian packages, create the commit and update the metadata. Install the debian packages, create the commit and update the metadata.
@@ -3071,7 +3106,7 @@ class PatchController(PatchService):
base_deployment = deployment_list[0] base_deployment = deployment_list[0]
base_release = self._release_basic_checks(base_deployment) base_release = self._release_basic_checks(base_deployment)
upgrade_commit_id = base_release.commit_id upgrade_commit_id = base_release.commit_id
if self._deploy_upgrade_start(base_release.sw_release, upgrade_commit_id): if self._deploy_upgrade_start(base_release.sw_release, upgrade_commit_id, **kwargs):
LOG.info("Finished releases %s deploy start" % deployment_list) LOG.info("Finished releases %s deploy start" % deployment_list)
else: else:
raise ValueError("_deploy_upgrade_start failed") raise ValueError("_deploy_upgrade_start failed")
@@ -3098,9 +3133,9 @@ class PatchController(PatchService):
thread = threading.Thread(target=run) thread = threading.Thread(target=run)
thread.start() thread.start()
def _precheck_before_start(self, deployment, release_version, is_patch, force=False, snapshot=False): def _precheck_before_start(self, deployment, release_version, is_patch, force=False, **kwargs):
LOG.info("Running deploy precheck.") LOG.info("Running deploy precheck.")
precheck_result = self._deploy_precheck(release_version, patch=is_patch, force=force, snapshot=snapshot) precheck_result = self._deploy_precheck(release_version, patch=is_patch, force=force, **kwargs)
if precheck_result.get('system_healthy') is None: if precheck_result.get('system_healthy') is None:
precheck_result["error"] = ( precheck_result["error"] = (
f"Fail to perform deploy precheck. Internal error has occurred.\n" f"Fail to perform deploy precheck. Internal error has occurred.\n"
@@ -3129,7 +3164,7 @@ class PatchController(PatchService):
with open(precheck_result_file, "w") as f: with open(precheck_result_file, "w") as f:
json.dump({"healthy": healthy, "timestamp": time.time()}, f) json.dump({"healthy": healthy, "timestamp": time.time()}, f)
def _should_run_precheck_prior_deploy_start(self, release_version, force, is_patch, snapshot): def _should_run_precheck_prior_deploy_start(self, release_version, force, is_patch, **kwargs):
# there is not precheck script in this state # there is not precheck script in this state
if self.pre_bootstrap: if self.pre_bootstrap:
return False return False
@@ -3143,7 +3178,7 @@ class PatchController(PatchService):
LOG.info("The precheck result file %s does not exist." % file_path) LOG.info("The precheck result file %s does not exist." % file_path)
return True return True
if snapshot: if kwargs:
return True return True
with open(file_path) as f: with open(file_path) as f:
@@ -3158,7 +3193,7 @@ class PatchController(PatchService):
@require_deploy_state([None], @require_deploy_state([None],
"There is already a deployment in progress ({state.value}). " "There is already a deployment in progress ({state.value}). "
"Please complete/delete the current deployment.") "Please complete/delete the current deployment.")
def software_deploy_start_api(self, deployment: str, force: bool, snapshot: bool, **kwargs) -> dict: def software_deploy_start_api(self, deployment: str, force: bool, **kwargs) -> dict:
""" """
to start deploy of a specified release. to start deploy of a specified release.
The operation implies deploying all undeployed dependency releases of The operation implies deploying all undeployed dependency releases of
@@ -3200,15 +3235,16 @@ class PatchController(PatchService):
LOG.warning("Using unknown hostname for local install: %s", hostname) LOG.warning("Using unknown hostname for local install: %s", hostname)
to_release = deploy_release.sw_release to_release = deploy_release.sw_release
if kwargs.get("options"):
if self._should_run_precheck_prior_deploy_start(to_release, force, is_patch, snapshot): kwargs["options"] = self._parse_and_sanitize_extra_options(kwargs.get("options"))
if self._should_run_precheck_prior_deploy_start(to_release, force, is_patch, **kwargs):
LOG.info("Executing software deploy precheck prior to software deploy start") LOG.info("Executing software deploy precheck prior to software deploy start")
if precheck_result := self._precheck_before_start( if precheck_result := self._precheck_before_start(
deployment, deployment,
to_release, to_release,
is_patch=is_patch, is_patch=is_patch,
force=force, force=force,
snapshot=snapshot, **kwargs
): ):
return precheck_result return precheck_result
self._safe_remove_precheck_result_file(to_release) self._safe_remove_precheck_result_file(to_release)
@@ -3281,11 +3317,11 @@ class PatchController(PatchService):
deploy_state.start(running_release, to_release, feed_repo, None, reboot_required) deploy_state.start(running_release, to_release, feed_repo, None, reboot_required)
else: else:
deploy_state.start(running_release, to_release, feed_repo, commit_id, deploy_state.start(running_release, to_release, feed_repo, commit_id,
reboot_required, snapshot=snapshot) reboot_required, **kwargs)
# Start applying the releases # Start applying the releases
upgrade = not is_patch upgrade = not is_patch
self.install_releases_thread(deployment_list, feed_repo, upgrade) self.install_releases_thread(deployment_list, feed_repo, upgrade, **kwargs)
msg_info += "%s is now starting, await for the states: " \ msg_info += "%s is now starting, await for the states: " \
"[deploy-start-done | deploy-start-failed] in " \ "[deploy-start-done | deploy-start-failed] in " \
@@ -3810,7 +3846,8 @@ class PatchController(PatchService):
system_mode = utils.get_platform_conf("system_mode") system_mode = utils.get_platform_conf("system_mode")
if system_mode == constants.SYSTEM_MODE_SIMPLEX: if system_mode == constants.SYSTEM_MODE_SIMPLEX:
deploy = self.db_api_instance.get_deploy_all()[0] deploy = self.db_api_instance.get_deploy_all()[0]
enabled_lvm_snapshots = deploy.get("snapshot") options = deploy.get("options", {})
enabled_lvm_snapshots = to_bool(options.get("snapshot"))
if enabled_lvm_snapshots: if enabled_lvm_snapshots:
LOG.info("LVM snapshots are enabled") LOG.info("LVM snapshots are enabled")
manager = lvm_snapshot.LVMSnapshotManager() manager = lvm_snapshot.LVMSnapshotManager()

View File

@@ -137,7 +137,7 @@ class Deploy(ABC):
@abstractmethod @abstractmethod
def create(self, from_release: str, to_release: str, feed_repo: str, def create(self, from_release: str, to_release: str, feed_repo: str,
commit_id: str, reboot_required: bool, state: DEPLOY_STATES): commit_id: str, reboot_required: bool, state: DEPLOY_STATES, **kwargs):
""" """
Create a new deployment entry. Create a new deployment entry.
@@ -267,8 +267,8 @@ class DeployHandler(Deploy):
"commit_id": commit_id, "commit_id": commit_id,
"reboot_required": reboot_required, "reboot_required": reboot_required,
"state": state.value, "state": state.value,
"options": kwargs.get("options")
} }
new_deploy.update(kwargs)
try: try:
data = get_software_filesystem_data() data = get_software_filesystem_data()

View File

@@ -1704,3 +1704,11 @@ def execute_agent_hooks(software_version, additional_data=None):
except Exception as e: except Exception as e:
LOG.exception("Error running agent hooks: %s" % str(e)) LOG.exception("Error running agent hooks: %s" % str(e))
raise raise
def to_bool(value):
if isinstance(value, bool):
return value
if isinstance(value, str):
return value.lower() == 'true'
return False