"software install-local" implementation for USM
This commit enables "software install-local" in a Debian env for a patch. Test Plan: [PASS] Verify software install-local installs the patch [PASS] Verify state machine for install-local workflow Story: 2010676 Task: 48607 Change-Id: I6fd44653210cecdc0e3e1131bfd4619a756556c8 Signed-off-by: Jessica Castelino <jessica.castelino@windriver.com>
This commit is contained in:
parent
a4bf3a1dc8
commit
0eb68e8416
@ -96,6 +96,16 @@ class SoftwareAPIController(object):
|
||||
|
||||
return result
|
||||
|
||||
@expose('json')
|
||||
@expose('query.xml', content_type='application/xml')
|
||||
def install_local(self):
|
||||
try:
|
||||
result = sc.software_install_local_api()
|
||||
except SoftwareError as e:
|
||||
return dict(error="Error: %s" % str(e))
|
||||
|
||||
return result
|
||||
|
||||
@expose('json')
|
||||
def is_completed(self, *args):
|
||||
return sc.is_completed(list(args))
|
||||
|
@ -12,6 +12,7 @@ import os
|
||||
import re
|
||||
import requests
|
||||
import signal
|
||||
import software.constants as constants
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
@ -20,8 +21,6 @@ import time
|
||||
from requests_toolbelt import MultipartEncoder
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import software.constants as constants
|
||||
|
||||
from tsconfig.tsconfig import SW_VERSION as RUNNING_SW_VERSION
|
||||
|
||||
api_addr = "127.0.0.1:5493"
|
||||
@ -716,6 +715,24 @@ def drop_host(args):
|
||||
return check_rc(req)
|
||||
|
||||
|
||||
def install_local(args): # pylint: disable=unused-argument
|
||||
# Ignore interrupts during this function
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
|
||||
url = "http://%s/software/install_local" % (api_addr)
|
||||
|
||||
headers = {}
|
||||
append_auth_token_if_required(headers)
|
||||
req = requests.get(url, headers=headers)
|
||||
|
||||
if args.debug:
|
||||
print_result_debug(req)
|
||||
else:
|
||||
print_software_op_result(req)
|
||||
|
||||
return check_rc(req)
|
||||
|
||||
|
||||
def release_upload_dir_req(args):
|
||||
# arg.release is a list
|
||||
release_dirs = args.release
|
||||
@ -1248,6 +1265,16 @@ def setup_argparse():
|
||||
nargs="+", # accepts a list
|
||||
help='Release ID to delete')
|
||||
|
||||
# -- software install-local ---------------
|
||||
cmd = commands.add_parser(
|
||||
'install-local',
|
||||
help='Trigger patch install/remove on the local host. ' +
|
||||
'This command can only be used for patch installation ' +
|
||||
'prior to initial configuration.'
|
||||
)
|
||||
cmd.set_defaults(cmd='install-local')
|
||||
cmd.set_defaults(func=install_local)
|
||||
|
||||
# --- software list ---------------------------
|
||||
cmd = commands.add_parser(
|
||||
'list',
|
||||
|
@ -55,6 +55,7 @@ from software.software_functions import package_dir
|
||||
from software.software_functions import repo_dir
|
||||
from software.software_functions import root_scripts_dir
|
||||
from software.software_functions import semantics_dir
|
||||
|
||||
from software.software_functions import SW_VERSION
|
||||
from software.software_functions import LOG
|
||||
from software.software_functions import audit_log_info
|
||||
@ -69,6 +70,7 @@ import software.messages as messages
|
||||
import software.constants as constants
|
||||
|
||||
from tsconfig.tsconfig import INITIAL_CONFIG_COMPLETE_FLAG
|
||||
from tsconfig.tsconfig import INITIAL_CONTROLLER_CONFIG_COMPLETE
|
||||
|
||||
CONF = oslo_cfg.CONF
|
||||
|
||||
@ -81,6 +83,9 @@ app_dependency_filename = "%s/%s" % (constants.SOFTWARE_STORAGE_DIR, app_depende
|
||||
|
||||
insvc_patch_restart_controller = "/run/software/.restart.software-controller"
|
||||
|
||||
ETC_HOSTS_FILE_PATH = "/etc/hosts"
|
||||
ETC_HOSTS_BACKUP_FILE_PATH = "/etc/hosts.patchbak"
|
||||
|
||||
stale_hosts = []
|
||||
pending_queries = []
|
||||
|
||||
@ -897,10 +902,89 @@ class PatchController(PatchService):
|
||||
LOG.exception(msg)
|
||||
raise SoftwareFail(msg)
|
||||
|
||||
def software_install_local_api(self):
|
||||
"""
|
||||
Trigger patch installation prior to configuration
|
||||
:return: dict of info, warning and error messages
|
||||
"""
|
||||
msg_info = ""
|
||||
msg_warning = ""
|
||||
msg_error = ""
|
||||
|
||||
# Check to see if initial configuration has completed
|
||||
if os.path.isfile(INITIAL_CONTROLLER_CONFIG_COMPLETE):
|
||||
# Disallow the install
|
||||
msg = "This command can only be used before initial system configuration."
|
||||
LOG.exception(msg)
|
||||
raise SoftwareFail(msg)
|
||||
|
||||
update_hosts_file = False
|
||||
|
||||
# Check to see if the controller hostname is already known.
|
||||
if not utils.gethostbyname(constants.CONTROLLER_FLOATING_HOSTNAME):
|
||||
update_hosts_file = True
|
||||
|
||||
# To allow software installation to occur before configuration, we need
|
||||
# to alias controller to localhost
|
||||
# There is a HOSTALIASES feature that would be preferred here, but it
|
||||
# unfortunately requires dnsmasq to be running, which it is not at this point.
|
||||
|
||||
if update_hosts_file:
|
||||
# Make a backup of /etc/hosts
|
||||
try:
|
||||
shutil.copy2(ETC_HOSTS_FILE_PATH, ETC_HOSTS_BACKUP_FILE_PATH)
|
||||
except Exception:
|
||||
msg = f"Error occurred while copying {ETC_HOSTS_FILE_PATH}."
|
||||
LOG.exception(msg)
|
||||
raise SoftwareFail(msg)
|
||||
|
||||
# Update /etc/hosts
|
||||
with open(ETC_HOSTS_FILE_PATH, 'a') as f:
|
||||
f.write("127.0.0.1 controller\n")
|
||||
|
||||
# Run the software install
|
||||
try:
|
||||
# Use the restart option of the sw-patch init script, which will
|
||||
# install patches but won't automatically reboot if the RR flag is set
|
||||
subprocess.check_output(['/etc/init.d/sw-patch', 'restart'])
|
||||
except subprocess.CalledProcessError:
|
||||
msg = "Failed to install patches."
|
||||
LOG.exception(msg)
|
||||
raise SoftwareFail(msg)
|
||||
|
||||
if update_hosts_file:
|
||||
# Restore /etc/hosts
|
||||
os.rename(ETC_HOSTS_BACKUP_FILE_PATH, ETC_HOSTS_FILE_PATH)
|
||||
|
||||
for release in sorted(list(self.release_data.metadata)):
|
||||
if self.release_data.metadata[release]["state"] == constants.DEPLOYING_START:
|
||||
self.release_data.metadata[release]["state"] = constants.DEPLOYED
|
||||
try:
|
||||
shutil.move("%s/%s-metadata.xml" % (deploying_start_dir, release),
|
||||
"%s/%s-metadata.xml" % (deployed_dir, release))
|
||||
except shutil.Error:
|
||||
msg = "Failed to move the metadata for %s" % release
|
||||
LOG.exception(msg)
|
||||
raise MetadataFail(msg)
|
||||
elif self.release_data.metadata[release]["state"] == constants.REMOVING:
|
||||
self.release_data.metadata[release]["state"] = constants.AVAILABLE
|
||||
try:
|
||||
shutil.move("%s/%s-metadata.xml" % (removing_dir, release),
|
||||
"%s/%s-metadata.xml" % (available_dir, release))
|
||||
except shutil.Error:
|
||||
msg = "Failed to move the metadata for %s" % release
|
||||
LOG.exception(msg)
|
||||
raise MetadataFail(msg)
|
||||
|
||||
msg_info += "Software installation is complete.\n"
|
||||
msg_info += "Please reboot before continuing with configuration."
|
||||
|
||||
return dict(info=msg_info, warning=msg_warning, error=msg_error)
|
||||
|
||||
def software_release_upload(self, release_files):
|
||||
"""
|
||||
Upload software release files
|
||||
:return:
|
||||
:return: dict of info, warning and error messages
|
||||
"""
|
||||
msg_info = ""
|
||||
msg_warning = ""
|
||||
@ -1015,7 +1099,9 @@ class PatchController(PatchService):
|
||||
msg_info += "%s is now uploaded\n" % release_id
|
||||
self.release_data.add_release(thisrelease)
|
||||
|
||||
if len(self.hosts) > 0:
|
||||
if not os.path.isfile(INITIAL_CONTROLLER_CONFIG_COMPLETE):
|
||||
self.release_data.metadata[release_id]["state"] = constants.AVAILABLE
|
||||
elif len(self.hosts) > 0:
|
||||
self.release_data.metadata[release_id]["state"] = constants.AVAILABLE
|
||||
else:
|
||||
self.release_data.metadata[release_id]["state"] = constants.UNKNOWN
|
||||
@ -1067,7 +1153,7 @@ class PatchController(PatchService):
|
||||
def software_release_delete_api(self, release_ids):
|
||||
"""
|
||||
Delete release(s)
|
||||
:return:
|
||||
:return: dict of info, warning and error messages
|
||||
"""
|
||||
msg_info = ""
|
||||
msg_warning = ""
|
||||
@ -1145,7 +1231,7 @@ class PatchController(PatchService):
|
||||
def patch_init_release_api(self, release):
|
||||
"""
|
||||
Create an empty repo for a new release
|
||||
:return:
|
||||
:return: dict of info, warning and error messages
|
||||
"""
|
||||
msg_info = ""
|
||||
msg_warning = ""
|
||||
@ -1204,7 +1290,7 @@ class PatchController(PatchService):
|
||||
def patch_query_what_requires(self, patch_ids):
|
||||
"""
|
||||
Query the known patches to see which have dependencies on the specified patches
|
||||
:return:
|
||||
:return: dict of info, warning and error messages
|
||||
"""
|
||||
msg_info = ""
|
||||
msg_warning = ""
|
||||
@ -1763,7 +1849,9 @@ class PatchController(PatchService):
|
||||
LOG.exception(msg)
|
||||
raise MetadataFail(msg)
|
||||
|
||||
if len(self.hosts) > 0:
|
||||
if not os.path.isfile(INITIAL_CONTROLLER_CONFIG_COMPLETE):
|
||||
self.release_data.metadata[release]["state"] = constants.DEPLOYING_START
|
||||
elif len(self.hosts) > 0:
|
||||
self.release_data.metadata[release]["state"] = constants.DEPLOYING_START
|
||||
else:
|
||||
self.release_data.metadata[release]["state"] = constants.UNKNOWN
|
||||
@ -1875,7 +1963,9 @@ class PatchController(PatchService):
|
||||
raise MetadataFail(msg)
|
||||
|
||||
# update state
|
||||
if len(self.hosts) > 0:
|
||||
if not os.path.isfile(INITIAL_CONTROLLER_CONFIG_COMPLETE):
|
||||
self.release_data.metadata[release]["state"] = constants.REMOVING
|
||||
elif len(self.hosts) > 0:
|
||||
self.release_data.metadata[release]["state"] = constants.REMOVING
|
||||
else:
|
||||
self.release_data.metadata[release]["state"] = constants.UNKNOWN
|
||||
@ -1901,7 +1991,9 @@ class PatchController(PatchService):
|
||||
raise MetadataFail(msg)
|
||||
|
||||
# update state
|
||||
if len(self.hosts) > 0:
|
||||
if not os.path.isfile(INITIAL_CONTROLLER_CONFIG_COMPLETE):
|
||||
self.release_data.metadata[deployment]["state"] = constants.DEPLOYING_START
|
||||
elif len(self.hosts) > 0:
|
||||
self.release_data.metadata[deployment]["state"] = constants.DEPLOYING_START
|
||||
else:
|
||||
self.release_data.metadata[deployment]["state"] = constants.UNKNOWN
|
||||
|
Loading…
Reference in New Issue
Block a user