deploy activate for major release
This change implements major release deploy activate operation. Execute the deploy activate scripts after verify all required state for major release deploy activate in separate sub-process. Deploy state is updated to "activate-done" when activate operation completes successfully and "activate-failed" otherwise. TCs: Passed: USM major release deploy activate completed successfully Passed: USM major release deploy activate failed Passed: USM major release deploy activate after activate failed Story: 2010676 Task: 50082 Change-Id: I8810674cdc3c67700ff7c419528704c4b8905f51 Signed-off-by: Bin Qian <bin.qian@windriver.com>
This commit is contained in:
parent
6decc21117
commit
61709d3d64
@ -37,6 +37,7 @@ console_scripts =
|
||||
software-agent = software.software_agent:main
|
||||
software-migrate = software.utilities.migrate:migrate
|
||||
software-deploy-update = software.utilities.update_deploy_state:update_state
|
||||
software-deploy-activate = software.utilities.activate:activate
|
||||
|
||||
|
||||
[wheel]
|
||||
|
@ -191,8 +191,9 @@ def require_deploy_state(require_states, prompt):
|
||||
return res
|
||||
else:
|
||||
msg = ""
|
||||
require_states_text = ", ".join([state.value for state in require_states])
|
||||
if prompt:
|
||||
msg = prompt.format(state=state, require_states=require_states)
|
||||
msg = prompt.format(state=state, require_states=require_states_text)
|
||||
raise InvalidOperation(msg)
|
||||
return exec_op
|
||||
return wrap
|
||||
|
@ -51,6 +51,7 @@ from software.exceptions import ReleaseInvalidRequest
|
||||
from software.exceptions import ReleaseValidationFailure
|
||||
from software.exceptions import ReleaseIsoDeleteFailure
|
||||
from software.exceptions import SoftwareServiceError
|
||||
from software.exceptions import InvalidOperation
|
||||
from software.release_data import reload_release_data
|
||||
from software.release_data import get_SWReleaseCollection
|
||||
from software.software_functions import collect_current_load_for_hosts
|
||||
@ -708,7 +709,7 @@ class SWMessageDeployStateChanged(messages.PatchMessage):
|
||||
self.valid = True
|
||||
self.agent = None
|
||||
|
||||
valid_agents = ['deploy-start']
|
||||
valid_agents = ['deploy-start', 'deploy-activate']
|
||||
if 'agent' in data:
|
||||
self.agent = data['agent']
|
||||
else:
|
||||
@ -722,7 +723,9 @@ class SWMessageDeployStateChanged(messages.PatchMessage):
|
||||
|
||||
valid_state = {
|
||||
DEPLOY_STATES.START_DONE.value: DEPLOY_STATES.START_DONE,
|
||||
DEPLOY_STATES.START_FAILED.value: DEPLOY_STATES.START_FAILED
|
||||
DEPLOY_STATES.START_FAILED.value: DEPLOY_STATES.START_FAILED,
|
||||
DEPLOY_STATES.ACTIVATE_FAILED.value: DEPLOY_STATES.ACTIVATE_FAILED,
|
||||
DEPLOY_STATES.ACTIVATE_DONE.value: DEPLOY_STATES.ACTIVATE_DONE
|
||||
}
|
||||
if 'deploy-state' in data and data['deploy-state']:
|
||||
deploy_state = data['deploy-state']
|
||||
@ -2290,7 +2293,9 @@ class PatchController(PatchService):
|
||||
deploy_state = DeployState.get_instance()
|
||||
state_event = {
|
||||
DEPLOY_STATES.START_DONE: deploy_state.start_done,
|
||||
DEPLOY_STATES.START_FAILED: deploy_state.start_failed
|
||||
DEPLOY_STATES.START_FAILED: deploy_state.start_failed,
|
||||
DEPLOY_STATES.ACTIVATE_DONE: deploy_state.activate_completed,
|
||||
DEPLOY_STATES.ACTIVATE_FAILED: deploy_state.activate_failed
|
||||
}
|
||||
if new_state in state_event:
|
||||
state_event[new_state]()
|
||||
@ -2680,11 +2685,66 @@ class PatchController(PatchService):
|
||||
return dict(info=msg_info, warning=msg_warning, error=msg_error)
|
||||
|
||||
def _activate(self):
|
||||
# TODO(bqian) activate the deployment
|
||||
deploy = self.db_api_instance.get_deploy_all()
|
||||
if deploy:
|
||||
deploy = deploy[0]
|
||||
else:
|
||||
msg = "Deployment is missing unexpectedly"
|
||||
raise InvalidOperation(msg)
|
||||
|
||||
deploying = ReleaseState(release_state=states.DEPLOYING)
|
||||
if deploying.is_major_release_deployment():
|
||||
return self._activate_major_release(deploy)
|
||||
else:
|
||||
return self.activate_patching_release()
|
||||
|
||||
def activate_patching_release(self):
|
||||
deploy_state = DeployState.get_instance()
|
||||
deploy_state.activate()
|
||||
# patching release activate operations go here
|
||||
deploy_state.activate_completed()
|
||||
return True
|
||||
|
||||
def _activate_major_release(self, deploy):
|
||||
cmd_path = "/usr/bin/software-deploy-activate"
|
||||
from_release = utils.get_major_release_version(deploy.get("from_release"))
|
||||
to_release = utils.get_major_release_version(deploy.get("to_release"))
|
||||
|
||||
upgrade_activate_cmd = [cmd_path, from_release, to_release]
|
||||
|
||||
try:
|
||||
LOG.info("starting subprocess %s" % ' '.join(upgrade_activate_cmd))
|
||||
subprocess.Popen(' '.join(upgrade_activate_cmd), start_new_session=True, shell=True)
|
||||
LOG.info("subprocess started")
|
||||
except subprocess.SubprocessError as e:
|
||||
LOG.error("Failed to start command: %s. Error %s" % (' '.join(upgrade_activate_cmd), e))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _check_pre_activate(self):
|
||||
# check current deployment, deploy to all hosts have completed,
|
||||
# the deploy state is host-done, or
|
||||
# activate-failed' as reattempt from a previous failed activate
|
||||
deploy_state = DeployState.get_deploy_state()
|
||||
if deploy_state not in [DEPLOY_STATES.HOST_DONE, DEPLOY_STATES.ACTIVATE_FAILED]:
|
||||
msg = "Must complete deploying all hosts before activating the deployment"
|
||||
raise InvalidOperation(msg)
|
||||
|
||||
deploy_hosts = self.db_api_instance.get_deploy_host()
|
||||
invalid_hosts = []
|
||||
for deploy_host in deploy_hosts:
|
||||
if deploy_host['state'] not in [states.DEPLOYED]:
|
||||
invalid_hosts.append(deploy_host)
|
||||
|
||||
if len(invalid_hosts) > 0:
|
||||
msg = "All hosts must have completed deployment before activating the deployment"
|
||||
for invalid_host in invalid_hosts:
|
||||
msg += "%s: %s\n" % (invalid_host["hostname"], invalid_host["state"])
|
||||
raise InvalidOperation(msg)
|
||||
|
||||
@require_deploy_state([DEPLOY_STATES.HOST_DONE, DEPLOY_STATES.ACTIVATE_FAILED],
|
||||
"Must complete deploying all hosts before activating the deployment")
|
||||
"Activate deployment only when current deployment state is {require_states}")
|
||||
def software_deploy_activate_api(self) -> dict:
|
||||
"""
|
||||
Activates the deployment associated with the release
|
||||
@ -2694,15 +2754,17 @@ class PatchController(PatchService):
|
||||
msg_warning = ""
|
||||
msg_error = ""
|
||||
|
||||
self._check_pre_activate()
|
||||
|
||||
deploy_state = DeployState.get_instance()
|
||||
deploy_state.activate()
|
||||
|
||||
if self._activate():
|
||||
deploy_state.activate_completed()
|
||||
msg_info += "Deployment has been activated.\n"
|
||||
else:
|
||||
try:
|
||||
self._activate()
|
||||
msg_info = "Deploy activate has started"
|
||||
except Exception:
|
||||
deploy_state.activate_failed()
|
||||
msg_error += "Deployment activation has failed.\n"
|
||||
raise
|
||||
|
||||
return dict(info=msg_info, warning=msg_warning, error=msg_error)
|
||||
|
||||
|
58
software/software/utilities/activate.py
Normal file
58
software/software/utilities/activate.py
Normal file
@ -0,0 +1,58 @@
|
||||
#
|
||||
# Copyright (c) 2024 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import argparse
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from software.states import DEPLOY_STATES
|
||||
from software.utilities.update_deploy_state import update_deploy_state
|
||||
from software.utilities.utils import execute_migration_scripts
|
||||
from software.utilities.utils import ACTION_ACTIVATE
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def do_activate(from_release, to_release):
|
||||
agent = 'deploy-activate'
|
||||
res = True
|
||||
state = DEPLOY_STATES.ACTIVATE_DONE.value
|
||||
try:
|
||||
execute_migration_scripts(from_release, to_release, ACTION_ACTIVATE)
|
||||
except Exception:
|
||||
state = DEPLOY_STATES.ACTIVATE_FAILED.value
|
||||
res = False
|
||||
finally:
|
||||
try:
|
||||
update_deploy_state(agent, deploy_state=state)
|
||||
if res:
|
||||
LOG.info("Deploy activate completed successfully")
|
||||
else:
|
||||
LOG.error("Deploy activate failed")
|
||||
except Exception:
|
||||
LOG.error("Update deploy state failed")
|
||||
res = False
|
||||
return res
|
||||
|
||||
|
||||
def activate():
|
||||
# this is the entry point to start data migration
|
||||
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
|
||||
parser.add_argument("from_release",
|
||||
default=False,
|
||||
help="From release")
|
||||
|
||||
parser.add_argument("to_release",
|
||||
default=False,
|
||||
help="To release")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if do_activate(args.from_release, args.to_release):
|
||||
exit(0)
|
||||
else:
|
||||
exit(1)
|
@ -32,7 +32,7 @@ def get_udp_socket(server_addr, server_port):
|
||||
return sock
|
||||
|
||||
|
||||
def update_deploy_state(server_addr, server_port, agent, deploy_state=None, host=None, host_state=None, timeout=1):
|
||||
def update_deploy_state(agent, deploy_state=None, host=None, host_state=None, timeout=1):
|
||||
"""
|
||||
Send MessageDeployStateChanged message to software-controller via
|
||||
upd packet, wait for ack or raise exception.
|
||||
@ -47,6 +47,10 @@ def update_deploy_state(server_addr, server_port, agent, deploy_state=None, host
|
||||
}
|
||||
"""
|
||||
|
||||
server_addr = "controller"
|
||||
cfg.read_config()
|
||||
server_port = cfg.controller_port
|
||||
|
||||
msg = {
|
||||
"msgtype": PATCHMSG_DEPLOY_STATE_CHANGED,
|
||||
"msgversion": 1,
|
||||
@ -104,9 +108,5 @@ def update_state():
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
server = "controller"
|
||||
cfg.read_config()
|
||||
server_port = cfg.controller_port
|
||||
update_deploy_state(server, int(server_port), args.agent,
|
||||
deploy_state=args.state,
|
||||
update_deploy_state(args.agent, deploy_state=args.state,
|
||||
host=args.host, host_state=args.host_state)
|
||||
|
Loading…
x
Reference in New Issue
Block a user