Support remote install for previous release centos-based subcloud

This commit adds support for remote installation of previous
centos-based subclouds.

The subcloud install logic now invokes either the debian-based
gen-bootloader-iso.sh script for current release subcloud installs, or
the gen-bootloader-iso-centos.sh script for the installing the previous
release subcloud.

We also clarify the log output of the gen-bootloader-iso command
shell invocation, to include all stdout/stderr output in the main
dcmanager.log file (via subprocess.run).

Includes a minor refactor of the utils naming in subcloud_install.py
to clarify which utils module is being used.

Test Plan
PASS
- Verify successful subcloud add which includes remote install with
  specified previous release (21.12). Verify that the centos-based
  miniboot bootimage.iso file is generated and installed via rvmc.
- Verify successful subcloud install with the specified current
  release. This includes the proper miniboot invocation for debian.
- Verify that any gen-bootloader-iso install issues/errors are now
  reflected in the dcmanager.log file

Story: 2010611
Task: 47784

Signed-off-by: Kyle MacLeod <kyle.macleod@windriver.com>
Change-Id: I2fa2bc56cc79ca8d17905337a8d81ad5c9a908ac
This commit is contained in:
Kyle MacLeod
2023-04-03 23:08:04 -04:00
parent 082328e05b
commit 7ae12fab78
4 changed files with 87 additions and 45 deletions

View File

@@ -29,7 +29,7 @@ from dccommon.drivers.openstack.keystone_v3 import KeystoneClient
from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
from dccommon import exceptions
from dccommon import install_consts
from dccommon import utils as common_utils
from dccommon import utils as dccommon_utils
from dcmanager.common import consts as common_consts
from dcmanager.common import utils
@@ -286,8 +286,10 @@ class SubcloudInstall(object):
def update_iso(self, override_path, values):
if not os.path.isdir(self.www_root):
os.mkdir(self.www_root, 0o755)
LOG.debug("update_iso: www_root: %s, values: %s, override_path: %s",
self.www_root, str(values), override_path)
path = None
software_version = str(values['software_version'])
try:
if parse.urlparse(values['image']).scheme:
url = values['image']
@@ -297,7 +299,7 @@ class SubcloudInstall(object):
filename = os.path.join(override_path, 'bootimage.iso')
if path and path.startswith(consts.LOAD_VAULT_DIR +
'/' + str(values['software_version'])):
'/' + software_version):
if os.path.exists(path):
# Reference known load in vault
LOG.info("Setting input_iso to load vault path %s" % path)
@@ -324,7 +326,9 @@ class SubcloudInstall(object):
resource=self.name,
msg=msg)
if common_utils.is_debian():
is_subcloud_debian = dccommon_utils.is_debian(software_version)
if is_subcloud_debian:
update_iso_cmd = [
GEN_ISO_COMMAND,
"--input", self.input_iso,
@@ -341,6 +345,7 @@ class SubcloudInstall(object):
"--id", self.name,
"--boot-hostname", self.name,
"--timeout", BOOT_MENU_TIMEOUT,
"--patches-from-iso",
]
for key in GEN_ISO_OPTIONS:
if key in values:
@@ -381,7 +386,11 @@ class SubcloudInstall(object):
else:
update_iso_cmd += [GEN_ISO_OPTIONS[key], str(values[key])]
if common_utils.is_centos():
if is_subcloud_debian:
# Get the base URL. ostree_repo is located within this path
base_url = os.path.join(self.get_image_base_url(), 'iso',
software_version)
else:
# create ks-addon.cfg
addon_cfg = os.path.join(override_path, 'ks-addon.cfg')
self.create_ks_conf_file(addon_cfg, values)
@@ -390,22 +399,21 @@ class SubcloudInstall(object):
# Get the base URL
base_url = os.path.join(self.get_image_base_url(), 'iso',
str(values['software_version']))
else:
# Get the base URL. ostree_repo is located within this path
base_url = os.path.join(self.get_image_base_url(), 'iso',
str(values['software_version']))
software_version)
update_iso_cmd += ['--base-url', base_url]
str_cmd = ' '.join(x for x in update_iso_cmd)
LOG.info("Running update_iso_cmd: %s", str_cmd)
try:
with open(os.devnull, "w") as fnull:
subprocess.check_call( # pylint: disable=E1102
update_iso_cmd, stdout=fnull, stderr=fnull)
except subprocess.CalledProcessError:
msg = "Failed to update iso %s, " % str(update_iso_cmd)
result = subprocess.run(update_iso_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
if result.returncode != 0:
msg = f'Failed to update iso: {str_cmd}'
LOG.error("%s returncode: %s, output: %s",
msg,
result.returncode,
result.stdout.decode('utf-8').replace('\n', ', '))
raise Exception(msg)
def cleanup(self):
@@ -416,7 +424,7 @@ class SubcloudInstall(object):
os.remove(self.input_iso)
if (self.www_root is not None and os.path.isdir(self.www_root)):
if common_utils.is_debian():
if dccommon_utils.is_debian():
cleanup_cmd = [
GEN_ISO_COMMAND,
"--id", self.name,
@@ -430,7 +438,6 @@ class SubcloudInstall(object):
"--www-root", self.www_root,
"--delete"
]
try:
with open(os.devnull, "w") as fnull:
subprocess.check_call( # pylint: disable=E1102
@@ -538,19 +545,21 @@ class SubcloudInstall(object):
if k in payload:
iso_values[k] = payload.get(k)
software_version = str(payload['software_version'])
iso_values['software_version'] = payload['software_version']
iso_values['image'] = payload['image']
override_path = os.path.join(override_path, self.name)
if not os.path.isdir(override_path):
os.mkdir(override_path, 0o755)
self.www_root = os.path.join(SUBCLOUD_ISO_PATH,
str(iso_values['software_version']))
software_version = str(payload['software_version'])
self.www_root = os.path.join(SUBCLOUD_ISO_PATH, software_version)
feed_path_rel_version = os.path.join(SUBCLOUD_FEED_PATH,
"rel-{version}".format(
version=software_version))
if common_utils.is_debian():
if dccommon_utils.is_debian(software_version):
self.check_ostree_mount(feed_path_rel_version)
# Clean up iso directory if it already exists
@@ -584,7 +593,11 @@ class SubcloudInstall(object):
if k in payload:
del payload[k]
if common_utils.is_centos():
# Only applicable for 22.06:
if (
dccommon_utils.is_centos(software_version)
and software_version == dccommon_utils.LAST_SW_VERSION_IN_CENTOS
):
# when adding a new subcloud, the subcloud will pull
# the file "packages_list" from the controller.
# The subcloud pulls from /var/www/pages/iso/<version>/.
@@ -624,7 +637,7 @@ class SubcloudInstall(object):
try:
# Since this is a long-running task we want to register
# for cleanup on process restart/SWACT.
common_utils.run_playbook(log_file, install_command)
dccommon_utils.run_playbook(log_file, install_command)
except exceptions.PlaybookExecutionFailed:
msg = ("Failed to install %s, check individual "
"log at %s or run %s for details"

View File

@@ -46,6 +46,8 @@ STALE_TOKEN_DURATION_STEP = 20
# Exitcode from 'timeout' command on timeout:
TIMEOUT_EXITCODE = 124
LAST_SW_VERSION_IN_CENTOS = "22.06"
class memoized(object):
"""Decorator.
@@ -253,11 +255,27 @@ def get_os_type(release_file=consts.OS_RELEASE_FILE):
return get_os_release(release_file)[0]
def is_debian():
def is_debian(software_version=None):
"""Check target version or underlying OS type.
Check either the given software_version (e.g. for checking a subcloud,
or prestaging operation), or the underlying OS type (for this running
instance)
"""
if software_version:
return not is_centos(software_version)
return get_os_type() == consts.OS_DEBIAN
def is_centos():
def is_centos(software_version=None):
"""Check target version or underlying OS type.
Check either the given software_version (e.g. for checking a subcloud,
or prestaging operation), or the underlying OS type (for this running
instance)
"""
if software_version:
return software_version <= LAST_SW_VERSION_IN_CENTOS
return get_os_type() == consts.OS_CENTOS

View File

@@ -34,6 +34,7 @@ from dccommon.drivers.openstack.sdk_platform import OpenStackDriver
from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
from dccommon.exceptions import PlaybookExecutionFailed
from dccommon.exceptions import PlaybookExecutionTimeout
from dccommon.utils import LAST_SW_VERSION_IN_CENTOS
from dccommon.utils import run_playbook
from dcmanager.common import consts
@@ -58,8 +59,6 @@ ANSIBLE_PRESTAGE_SUBCLOUD_IMAGES_PLAYBOOK = \
"/usr/share/ansible/stx-ansible/playbooks/prestage_images.yml"
ANSIBLE_PRESTAGE_INVENTORY_SUFFIX = '_prestage_inventory.yml'
LAST_SW_VERSION_IN_CENTOS = "22.06"
def is_deploy_status_prestage(deploy_status):
return deploy_status in (consts.PRESTAGE_STATE_PREPARE,

View File

@@ -474,7 +474,7 @@ class SubcloudManager(manager.Manager):
ansible_subcloud_inventory_file,
subcloud.software_version)
apply_thread = threading.Thread(
target=self.run_deploy,
target=self.run_deploy_thread,
args=(subcloud, payload, context,
None, None, None, rehome_command))
else:
@@ -489,7 +489,7 @@ class SubcloudManager(manager.Manager):
ansible_subcloud_inventory_file,
subcloud.software_version)
apply_thread = threading.Thread(
target=self.run_deploy,
target=self.run_deploy_thread,
args=(subcloud, payload, context,
install_command, apply_command, deploy_command))
@@ -536,7 +536,7 @@ class SubcloudManager(manager.Manager):
del payload['sysadmin_password']
apply_thread = threading.Thread(
target=self.run_deploy,
target=self.run_deploy_thread,
args=(subcloud, payload, context, None, None, deploy_command))
apply_thread.start()
return db_api.subcloud_db_model_to_dict(subcloud)
@@ -612,7 +612,7 @@ class SubcloudManager(manager.Manager):
management_subnet = utils.get_management_subnet(payload)
network_reconfig = management_subnet != subcloud.management_subnet
apply_thread = threading.Thread(
target=self.run_deploy,
target=self.run_deploy_thread,
args=(subcloud, payload, context,
install_command, apply_command, deploy_command,
None, network_reconfig))
@@ -1261,16 +1261,27 @@ class SubcloudManager(manager.Manager):
except Exception as e:
LOG.exception(e)
# TODO(kmacleod) add outer try/except here to catch and log unexpected
# exception. As this stands, any uncaught exception is a silent (unlogged)
# failure
def run_deploy(self, subcloud, payload, context,
install_command=None, apply_command=None,
deploy_command=None, rehome_command=None,
network_reconfig=None):
def run_deploy_thread(self, subcloud, payload, context,
install_command=None, apply_command=None,
deploy_command=None, rehome_command=None,
network_reconfig=None):
try:
self._run_deploy(subcloud, payload, context,
install_command, apply_command,
deploy_command, rehome_command,
network_reconfig)
except Exception as ex:
LOG.exception("run_deploy failed")
raise ex
log_file = os.path.join(consts.DC_ANSIBLE_LOG_DIR, subcloud.name) + \
'_playbook_output.log'
def _run_deploy(self, subcloud, payload, context,
install_command, apply_command,
deploy_command, rehome_command,
network_reconfig):
log_file = (
os.path.join(consts.DC_ANSIBLE_LOG_DIR, subcloud.name)
+ "_playbook_output.log"
)
if install_command:
install_success = self._run_subcloud_install(
context, subcloud, install_command,
@@ -1285,9 +1296,10 @@ class SubcloudManager(manager.Manager):
context, subcloud.id,
deploy_status=consts.DEPLOY_STATE_BOOTSTRAPPING,
error_description=consts.ERROR_DESC_EMPTY)
except Exception as e:
LOG.exception(e)
raise e
except Exception:
LOG.error("DB subcloud_update failed")
# exception is logged above
raise
# Run the ansible boostrap-subcloud playbook
LOG.info("Starting bootstrap of %s" % subcloud.name)