Remove upgrade scripts from config repo
Since upgrade is the responsability of USM the upgrade scripts need to be moved to the update repo. This commit removes the upgrade-scripts from this repo. Commit [1] has already been created in update repo. The scripts will still be located under /usr/local/share/upgrade.d . Test Plan: PASS: Build-pkgs && build-image PASS: Upgrade from 24.09 to 25.09 PASS: Install/bootstrap 25.09 PASS: Check if /usr/local/share/upgrade.d have the same scripts. Story: 2011357 Task: 52193 [1]: https://review.opendev.org/c/starlingx/update/+/949948 Depends-On: https://review.opendev.org/c/starlingx/update/+/949948 Change-Id: Id7e91abd6cfad4f5388dc562f2275d2e692578e7 Signed-off-by: Luis Eduardo Bonatti <luizeduardo.bonatti@windriver.com>
This commit is contained in:

committed by
Luis Eduardo Bonatti

parent
c0016d6b4b
commit
38ba8575ba
@ -1,72 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script adds system service parameters for service kubernetes
|
||||
# for sections kube_scheduler and kube_controller_manager.
|
||||
#
|
||||
# This script relies on 'kubernetes-service-parameters-apply.py'
|
||||
# to apply the parameters to kubeapi, needing to be executed before it.
|
||||
#
|
||||
# As this script does not restart any kubernetes components, we do not
|
||||
# need to run k8s health check here.
|
||||
#
|
||||
|
||||
NAME=$(basename "$0")
|
||||
|
||||
# The migration scripts are passed these parameters:
|
||||
FROM_RELEASE=$1
|
||||
TO_RELEASE=$2
|
||||
ACTION=$3
|
||||
|
||||
SOFTWARE_LOG_PATH="/var/log/software.log"
|
||||
|
||||
function log {
|
||||
echo "$(date -Iseconds | cut -d'+' -f1): ${NAME}[$$]: INFO: $*" >> "${SOFTWARE_LOG_PATH}" 2>&1
|
||||
}
|
||||
|
||||
log "Disable leader election for kube-scheduler and kube-controller-manager"\
|
||||
"from $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
|
||||
if [[ "${ACTION}" == "activate" ]]; then
|
||||
source /etc/platform/platform.conf
|
||||
if [[ "${nodetype}" == "controller" ]] && [[ "${system_mode}" == "simplex" ]]; then
|
||||
source /etc/platform/openrc
|
||||
for section in kube_scheduler kube_controller_manager; do
|
||||
value=$(system service-parameter-list --service kubernetes \
|
||||
--section "${section}" --format value | awk '/leader-elect/ {print $5}')
|
||||
if [[ "${value}" == "false" ]]; then
|
||||
log "Service parameter leader-elect=false already exists for section ${section}."\
|
||||
"Nothing to do."
|
||||
elif [[ "${value}" == "" ]]; then
|
||||
system service-parameter-add kubernetes "${section}" leader-elect=false
|
||||
RC=$?
|
||||
if [ ${RC} == 0 ]; then
|
||||
log "Successfully added service parameter leader-elect=false for ${section}"
|
||||
else
|
||||
log "Command service-parameter-add failed for section ${section}."\
|
||||
"Exiting for manual intervention or retry of the activation."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# 'true' or any garbage value
|
||||
system service-parameter-modify kubernetes "${section}" leader-elect=false
|
||||
RC=$?
|
||||
if [ ${RC} == 0 ]; then
|
||||
log "Successfully updated service parameter leader-elect=false for ${section}"
|
||||
else
|
||||
log "Command service-parameter-modify failed for section ${section}."\
|
||||
"Exiting for manual intervention or retry of the activation."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
log "No actions required for ${system_mode}-${nodetype}"
|
||||
fi
|
||||
else
|
||||
log "No actions required from release ${FROM_RELEASE} to ${TO_RELEASE} with action ${ACTION}"
|
||||
fi
|
||||
|
||||
exit 0
|
@ -1,158 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script updates the node IP addresses in sysinv DB tables. Only admin
|
||||
# network entries and only AIO-SX systems will be updated with the following
|
||||
# actions:
|
||||
# - address_pools: update controller0_address_id and controller1_address_id
|
||||
# to None
|
||||
# - addresses: update floating address IPv4 and IPv6 entries' interface_id
|
||||
# with controller-0's entries' interface_id
|
||||
# - addresses: delete IPv4 and IPv6 controller-0 and controller-1 entries'
|
||||
# interface_id
|
||||
#
|
||||
|
||||
import logging as LOG
|
||||
import sys
|
||||
|
||||
from packaging import version
|
||||
import psycopg2
|
||||
from six.moves import configparser
|
||||
|
||||
DEFAULT_POSTGRES_PORT = 5432
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
postgres_port = DEFAULT_POSTGRES_PORT
|
||||
arg = 1
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
postgres_port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
log_format = ('%(asctime)s: ' + '[%(process)s]: '
|
||||
'%(filename)s(%(lineno)s): %(levelname)s: %(message)s')
|
||||
LOG.basicConfig(filename="/var/log/software.log",
|
||||
format=log_format, level=LOG.INFO, datefmt="%FT%T")
|
||||
LOG.info(
|
||||
"%s invoked from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action)
|
||||
)
|
||||
res = 0
|
||||
to_release_version = version.Version(to_release)
|
||||
target_version = version.Version("25.09")
|
||||
if action == 'migrate' and to_release_version == target_version:
|
||||
if get_system_mode() == "simplex":
|
||||
try:
|
||||
conn = psycopg2.connect("dbname=sysinv user=postgres port=%s"
|
||||
% postgres_port)
|
||||
del_admin_node_addresses(conn)
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
LOG.exception("Error: {}".format(e))
|
||||
res = 1
|
||||
return res
|
||||
|
||||
|
||||
def del_admin_node_addresses(conn):
|
||||
query = (
|
||||
"SELECT address_pools.id,controller0_address_id,controller1_address_id"
|
||||
",floating_address_id "
|
||||
"FROM address_pools "
|
||||
"JOIN network_addresspools ON address_pools.id "
|
||||
"= network_addresspools.address_pool_id "
|
||||
"JOIN networks ON network_addresspools.network_id = networks.id "
|
||||
"WHERE networks.type = 'admin';"
|
||||
)
|
||||
res1 = db_query(conn, query)
|
||||
LOG.info("Number of address_pools entries found: %s" % len(res1))
|
||||
|
||||
controller0_ids = ",".join([str(e[1]) for e in res1 if e[1]])
|
||||
if not controller0_ids:
|
||||
LOG.info("Nothing to change")
|
||||
return
|
||||
|
||||
query = (
|
||||
"SELECT interface_id "
|
||||
"FROM addresses "
|
||||
"WHERE id IN (%s);" % controller0_ids
|
||||
)
|
||||
res2 = db_query(conn, query)
|
||||
c0_interface_ids = tuple([e[0] for e in res2])
|
||||
LOG.info("interface_id found in addresses: %s" % (c0_interface_ids,))
|
||||
|
||||
idx = 0
|
||||
for entry in res1:
|
||||
address_pools_id = entry[0]
|
||||
node_ids = entry[1:3]
|
||||
floating_id = entry[3]
|
||||
LOG.info("Found admin controller-0 and controller-1 IDs = %s"
|
||||
% (node_ids,))
|
||||
query = (
|
||||
"UPDATE address_pools "
|
||||
"SET controller0_address_id = NULL, controller1_address_id = NULL "
|
||||
"WHERE id = %s;" % address_pools_id
|
||||
)
|
||||
db_update(conn, query)
|
||||
query = (
|
||||
"UPDATE addresses "
|
||||
"SET interface_id = %s "
|
||||
"WHERE id = %s;" % (c0_interface_ids[idx], floating_id)
|
||||
)
|
||||
db_update(conn, query)
|
||||
query = (
|
||||
"DELETE FROM addresses "
|
||||
"WHERE id IN %s;" % (node_ids,)
|
||||
)
|
||||
db_update(conn, query)
|
||||
idx += 1
|
||||
|
||||
LOG.info("Admin addresses deleted from address_pools and addresses tables "
|
||||
"with success")
|
||||
|
||||
|
||||
def db_query(conn, query):
|
||||
result = []
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query)
|
||||
for rec in cur:
|
||||
result.append(rec)
|
||||
return result
|
||||
|
||||
|
||||
def db_update(conn, query):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def get_system_mode():
|
||||
ini_str = '[DEFAULT]\n' + open('/etc/platform/platform.conf', 'r').read()
|
||||
|
||||
config_applied = configparser.RawConfigParser()
|
||||
config_applied.read_string(ini_str)
|
||||
|
||||
if config_applied.has_option('DEFAULT', 'system_mode'):
|
||||
system_mode = config_applied.get('DEFAULT', 'system_mode')
|
||||
else:
|
||||
system_mode = None
|
||||
|
||||
return system_mode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,90 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script will remove load, host_upgrade and software_upgrade
|
||||
# database table
|
||||
#
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from packaging import version
|
||||
import psycopg2
|
||||
|
||||
from controllerconfig.common.usm_log import configure_logging
|
||||
|
||||
DEFAULT_POSTGRES_PORT = 5432
|
||||
|
||||
LOG = logging.getLogger('main_logger')
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
postgres_port = DEFAULT_POSTGRES_PORT
|
||||
arg = 1
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
postgres_port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
configure_logging()
|
||||
LOG.info(
|
||||
"%s invoked from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action)
|
||||
)
|
||||
res = 0
|
||||
to_release_version = version.Version(to_release)
|
||||
minimum_version = version.Version("25.09")
|
||||
if action == 'migrate' and to_release_version == minimum_version:
|
||||
try:
|
||||
conn = psycopg2.connect("dbname=sysinv user=postgres port=%s"
|
||||
% postgres_port)
|
||||
delete_software_upgrade_database(conn)
|
||||
delete_host_upgrade_database(conn)
|
||||
delete_load_database(conn)
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
LOG.exception("Error: {}".format(e))
|
||||
res = 1
|
||||
return res
|
||||
|
||||
|
||||
def delete_load_database(conn):
|
||||
delete_cmd = "drop table if exists loads;"
|
||||
db_update(conn, delete_cmd)
|
||||
LOG.info("Loads table removed with success")
|
||||
|
||||
|
||||
def delete_host_upgrade_database(conn):
|
||||
delete_cmd = "drop table if exists host_upgrade;"
|
||||
db_update(conn, delete_cmd)
|
||||
LOG.info("Host_upgrade table removed with success")
|
||||
|
||||
|
||||
def delete_software_upgrade_database(conn):
|
||||
delete_cmd = "drop table if exists software_upgrade;"
|
||||
db_update(conn, delete_cmd)
|
||||
LOG.info("Software_upgrade table removed with success")
|
||||
|
||||
|
||||
def db_update(conn, query):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query)
|
||||
conn.commit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,105 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# The purpose of this script is to populate the sw_version
|
||||
# field on i_host table.
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import psycopg2
|
||||
from six.moves import configparser
|
||||
|
||||
from controllerconfig.common.usm_log import configure_logging
|
||||
|
||||
CONTROLLER_0_HOSTNAME = "controller-0"
|
||||
CONTROLLER_1_HOSTNAME = "controller-1"
|
||||
DEFAULT_POSTGRES_PORT = 5432
|
||||
|
||||
LOG = logging.getLogger('main_logger')
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
postgres_port = DEFAULT_POSTGRES_PORT
|
||||
arg = 1
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
postgres_port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
configure_logging()
|
||||
LOG.info(
|
||||
"%s invoked from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action)
|
||||
)
|
||||
res = 0
|
||||
if action == 'migrate':
|
||||
try:
|
||||
conn = psycopg2.connect("dbname=sysinv user=postgres port=%s"
|
||||
% postgres_port)
|
||||
populate_ihost_sw_version(conn, to_release)
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
LOG.exception("Error: {}".format(e))
|
||||
res = 1
|
||||
return res
|
||||
|
||||
|
||||
def populate_ihost_sw_version(conn, to_release):
|
||||
"""
|
||||
Populate the sw_version field of i_host table for simplex
|
||||
"""
|
||||
hostname = CONTROLLER_1_HOSTNAME
|
||||
if get_system_mode() == "simplex":
|
||||
hostname = CONTROLLER_0_HOSTNAME
|
||||
update_query = ("UPDATE i_host set sw_version = %s WHERE "
|
||||
"hostname = '%s'" % (to_release,
|
||||
hostname))
|
||||
db_update(conn, update_query)
|
||||
LOG.info("Updated sw_version to %s on %s" % (to_release, hostname))
|
||||
|
||||
|
||||
def get_system_mode():
|
||||
ini_str = '[DEFAULT]\n' + open('/etc/platform/platform.conf', 'r').read()
|
||||
|
||||
config_applied = configparser.RawConfigParser()
|
||||
config_applied.read_string(ini_str)
|
||||
|
||||
if config_applied.has_option('DEFAULT', 'system_mode'):
|
||||
system_mode = config_applied.get('DEFAULT', 'system_mode')
|
||||
else:
|
||||
system_mode = None
|
||||
|
||||
return system_mode
|
||||
|
||||
|
||||
def db_update(conn, query):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def db_query(conn, query):
|
||||
result = []
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query)
|
||||
for rec in cur:
|
||||
result.append(rec)
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,120 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import logging
|
||||
from oslo_config import cfg
|
||||
import os
|
||||
from six.moves import configparser
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from cgtsclient import client as cgts_client
|
||||
from controllerconfig.common.usm_log import configure_logging
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger('main_logger')
|
||||
|
||||
|
||||
class CgtsClient(object):
|
||||
SYSINV_API_VERSION = "1"
|
||||
|
||||
def __init__(self):
|
||||
self._sysinv_client = None
|
||||
|
||||
@property
|
||||
def sysinv(self):
|
||||
if not self._sysinv_client:
|
||||
self._sysinv_client = cgts_client.get_client(
|
||||
self.SYSINV_API_VERSION,
|
||||
os_auth_token=os.environ.get("OS_AUTH_TOKEN"),
|
||||
system_url=os.environ.get("SYSTEM_URL"),
|
||||
)
|
||||
return self._sysinv_client
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
arg = 1
|
||||
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
# optional port parameter for USM upgrade
|
||||
# port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
configure_logging()
|
||||
LOG.info("%s invoked from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action))
|
||||
res = 0
|
||||
if action == "activate":
|
||||
try:
|
||||
res = activate_keystone()
|
||||
except Exception:
|
||||
LOG.error("Activate keystone action failed")
|
||||
res = 1
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def get_system_mode():
|
||||
ini_str = '[DEFAULT]\n' + open('/etc/platform/platform.conf', 'r').read()
|
||||
|
||||
config_applied = configparser.RawConfigParser()
|
||||
config_applied.read_string(ini_str)
|
||||
|
||||
if config_applied.has_option('DEFAULT', 'system_mode'):
|
||||
system_mode = config_applied.get('DEFAULT', 'system_mode')
|
||||
else:
|
||||
system_mode = None
|
||||
|
||||
return system_mode
|
||||
|
||||
|
||||
def get_shared_services():
|
||||
client = CgtsClient()
|
||||
isystem = client.sysinv.isystem.list()[0]
|
||||
shared_services = isystem.capabilities.get('shared_services', '')
|
||||
return shared_services
|
||||
|
||||
|
||||
def activate_keystone():
|
||||
if get_system_mode() != "simplex":
|
||||
try:
|
||||
shared_services = get_shared_services()
|
||||
except Exception:
|
||||
LOG.exception("Failed to get shared services")
|
||||
return 1
|
||||
|
||||
if 'identity' not in shared_services:
|
||||
keystone_cmd = ('keystone-manage db_sync --contract')
|
||||
try:
|
||||
subprocess.check_call([keystone_cmd], shell=True)
|
||||
except subprocess.CalledProcessError:
|
||||
msg = "Failed to contract Keystone databases for upgrade."
|
||||
LOG.exception(msg)
|
||||
return 1
|
||||
except Exception:
|
||||
LOG.exception("Failed to execute command %s" % keystone_cmd)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2023-2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import yaml
|
||||
|
||||
from controllerconfig.common.usm_log import configure_logging
|
||||
|
||||
LOG = logging.getLogger('main_logger')
|
||||
|
||||
|
||||
def get_list_of_keys(from_release, to_release):
|
||||
keys = {"static": [], "secure_static": []}
|
||||
return keys
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
arg = 1
|
||||
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
# optional port parameter for USM upgrade
|
||||
# port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
configure_logging()
|
||||
|
||||
LOG.info("%s invoked from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action))
|
||||
res = 0
|
||||
if action == "migrate":
|
||||
try:
|
||||
res = do_update(from_release, to_release)
|
||||
except Exception:
|
||||
LOG.exception("Updating static hieradata action failed")
|
||||
res = 1
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def do_update(from_release, to_release):
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
_do_update_under_temp(from_release, to_release, tempdir)
|
||||
|
||||
|
||||
def _do_update_under_temp(from_release, to_release, tempdir):
|
||||
SYSTEM_STATIC_FILE = "static.yaml"
|
||||
SECURE_STATIC_FILE = "secure_static.yaml"
|
||||
HIERADATA_PATH = "/opt/platform/puppet/%s/hieradata"
|
||||
|
||||
# copy static hieradata yaml files to tempdir
|
||||
system_static_file = \
|
||||
os.path.join(HIERADATA_PATH % to_release, SYSTEM_STATIC_FILE)
|
||||
secure_static_file = \
|
||||
os.path.join(HIERADATA_PATH % to_release, SECURE_STATIC_FILE)
|
||||
tmp_system_static_file = os.path.join(tempdir, SYSTEM_STATIC_FILE)
|
||||
tmp_secure_static_file = os.path.join(tempdir, SECURE_STATIC_FILE)
|
||||
files_to_copy = {system_static_file: tmp_system_static_file,
|
||||
secure_static_file: tmp_secure_static_file}
|
||||
|
||||
for src in files_to_copy:
|
||||
dest = files_to_copy[src]
|
||||
try:
|
||||
shutil.copyfile(src, dest)
|
||||
except IOError as e:
|
||||
LOG.error("Failed copying file %s to %s. Error %s", src, dest, e)
|
||||
raise
|
||||
|
||||
# generate static config to /opt/platform/puppet/<ver>/hieradata
|
||||
cmd = ["sysinv-puppet", "create-static-config"]
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
|
||||
out, err = process.communicate()
|
||||
if process.returncode != 0:
|
||||
msg = "Failed to generate static config. Command output: \n%s" % err
|
||||
LOG.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
src_file_mapping = {"static": system_static_file,
|
||||
"secure_static": secure_static_file}
|
||||
|
||||
tmp_file_mapping = {"static": tmp_system_static_file,
|
||||
"secure_static": tmp_secure_static_file}
|
||||
list_of_keys = get_list_of_keys(from_release, to_release)
|
||||
|
||||
# find the new generated static data and update the static hieradata
|
||||
# from previous release
|
||||
for tag in list_of_keys:
|
||||
keys = list_of_keys[tag]
|
||||
if len(keys) > 0:
|
||||
tmp_file = tmp_file_mapping[tag]
|
||||
src_file = src_file_mapping[tag]
|
||||
# read the key/value from src_file
|
||||
# (generated by sysinv-puppet create-static-config)
|
||||
# at /opt/platform/puppet/hieradata/<to_release>
|
||||
# write the key/value to tmp_file at temp directory
|
||||
with open(src_file, "r") as src:
|
||||
try:
|
||||
src_data = yaml.load(src, Loader=yaml.Loader)
|
||||
except Exception as e:
|
||||
LOG.error("Failed to load %s. Error %s" % (src_file, e))
|
||||
raise
|
||||
|
||||
with open(tmp_file, "r") as dest:
|
||||
try:
|
||||
dest_data = yaml.load(dest, Loader=yaml.Loader)
|
||||
except Exception as e:
|
||||
LOG.error("Failed to load %s. Error %s" % (tmp_file, e))
|
||||
raise
|
||||
|
||||
for key in keys:
|
||||
if key in src_data:
|
||||
dest_data[key] = src_data[key]
|
||||
else:
|
||||
LOG.warn("Expect %s generated in %s, but is not found" %
|
||||
(key, src_file))
|
||||
|
||||
with open(tmp_file, "w") as dest:
|
||||
try:
|
||||
yaml.dump(dest_data, dest, default_flow_style=False)
|
||||
except Exception as e:
|
||||
LOG.error("Failed to update %s. Error %s" %
|
||||
(tmp_file, e))
|
||||
raise
|
||||
|
||||
# copy the updated static hieradata yaml files to hieradata directory
|
||||
# of to release
|
||||
dest_system_static_file = \
|
||||
os.path.join(HIERADATA_PATH % to_release, SYSTEM_STATIC_FILE)
|
||||
dest_secure_static_file = \
|
||||
os.path.join(HIERADATA_PATH % to_release, SECURE_STATIC_FILE)
|
||||
dest_file_mapping = {"static": dest_system_static_file,
|
||||
"secure_static": dest_secure_static_file}
|
||||
for tag in ["static", "secure_static"]:
|
||||
try:
|
||||
shutil.copyfile(tmp_file_mapping[tag], dest_file_mapping[tag])
|
||||
except Exception as e:
|
||||
msg = "Failed to copy file %s to %s. Error %s" % (
|
||||
tmp_file_mapping[tag],
|
||||
dest_file_mapping[tag],
|
||||
e)
|
||||
LOG.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,48 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script disables NFV-VIM Web Server. From version 25.x onwards,
|
||||
# the web server will stay disabled by default in order to optimize
|
||||
# memory and CPU consumption of the host.
|
||||
#
|
||||
# The user can manually reactivate it issuing the command:
|
||||
# "sm-provision service-group-member vim-services vim-webserver"
|
||||
#
|
||||
|
||||
# shellcheck disable=SC2206
|
||||
|
||||
# The script receives these parameters:
|
||||
FROM_RELEASE=$1
|
||||
TO_RELEASE=$2
|
||||
ACTION=$3
|
||||
|
||||
SOFTWARE_LOG_PATH="/var/log/software.log"
|
||||
FROM_RELEASE_ARR=(${FROM_RELEASE//./ })
|
||||
FROM_RELEASE_MAJOR=${FROM_RELEASE_ARR[0]}
|
||||
TO_RELEASE_ARR=(${TO_RELEASE//./ })
|
||||
TO_RELEASE_MAJOR=${TO_RELEASE_ARR[0]}
|
||||
|
||||
# Default logging method extracted from script #02
|
||||
function log {
|
||||
echo "$(date -Iseconds | cut -d'+' -f1): ${NAME}[$$]: INFO: $*" \
|
||||
>> "${SOFTWARE_LOG_PATH}" 2>&1
|
||||
}
|
||||
|
||||
if [[ "${ACTION}" == "migrate" ]] && \
|
||||
[ ${FROM_RELEASE_MAJOR} -lt 25 ] && \
|
||||
[ ${TO_RELEASE_MAJOR} -ge 25 ]; then
|
||||
|
||||
log Disabling the NFV-VIM Web Server...
|
||||
|
||||
sm-deprovision service-group-member vim-services vim-webserver
|
||||
ret_value=$?
|
||||
|
||||
[ $ret_value -eq 0 ] && log NFV-VIM Web Server successfully disabled
|
||||
exit $ret_value
|
||||
|
||||
else
|
||||
log No actions required from $FROM_RELEASE to $TO_RELEASE with action $ACTION
|
||||
fi
|
@ -1,316 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import logging as LOG
|
||||
import os
|
||||
import psycopg2
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from cgtsclient import client as cgts_client
|
||||
from netaddr import valid_ipv4
|
||||
from netaddr import valid_ipv6
|
||||
from sysinv.common import constants as sysinv_constants
|
||||
from wsme import types as wtypes
|
||||
|
||||
|
||||
DEFAULT_POSTGRES_PORT = 5432
|
||||
LOG_FILE = "/var/log/software.log"
|
||||
|
||||
|
||||
# CgtsClient class to handle API interactions
|
||||
class CgtsClient(object):
|
||||
SYSINV_API_VERSION = 1
|
||||
|
||||
def __init__(self):
|
||||
self.conf = {}
|
||||
self._sysinv = None
|
||||
|
||||
# Loading credentials and configurations from environment variables
|
||||
# typically set in OpenStack
|
||||
source_command = 'source /etc/platform/openrc && env'
|
||||
|
||||
with open(os.devnull, "w") as fnull:
|
||||
proc = subprocess.Popen(
|
||||
['bash', '-c', source_command],
|
||||
stdout=subprocess.PIPE, stderr=fnull,
|
||||
universal_newlines=True)
|
||||
|
||||
# Strip the configurations starts with 'OS_' and change
|
||||
# the value to lower
|
||||
for line in proc.stdout:
|
||||
key, _, value = line.partition("=")
|
||||
if key.startswith('OS_'):
|
||||
self.conf[key[3:].lower()] = value.strip()
|
||||
|
||||
proc.communicate()
|
||||
|
||||
@property
|
||||
def sysinv(self):
|
||||
if not self._sysinv:
|
||||
self._sysinv = cgts_client.get_client(
|
||||
self.SYSINV_API_VERSION,
|
||||
os_username=self.conf['username'],
|
||||
os_password=self.conf['password'],
|
||||
os_auth_url=self.conf['auth_url'],
|
||||
os_project_name=self.conf['project_name'],
|
||||
os_project_domain_name=self.conf['project_domain_name'],
|
||||
os_user_domain_name=self.conf['user_domain_name'],
|
||||
os_region_name=self.conf['region_name'],
|
||||
os_service_type='platform',
|
||||
os_endpoint_type='internal')
|
||||
return self._sysinv
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
postgres_port = DEFAULT_POSTGRES_PORT
|
||||
arg = 1
|
||||
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
# optional port parameter for USM upgrade
|
||||
postgres_port = sys.argv[arg]
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
log_format = ('%(asctime)s: ' + '[%(process)s]: '
|
||||
'%(filename)s(%(lineno)s): %(levelname)s: %(message)s')
|
||||
LOG.basicConfig(filename=LOG_FILE, format=log_format, level=LOG.INFO,
|
||||
datefmt="%FT%T")
|
||||
|
||||
LOG.info("%s invoked from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action))
|
||||
|
||||
if action == "migrate" and from_release == "24.09":
|
||||
LOG.info("Create service parameter dns host record for "
|
||||
"registry.central")
|
||||
|
||||
conn = None
|
||||
try:
|
||||
client = CgtsClient()
|
||||
virtual_system = check_virtual_system(client)
|
||||
|
||||
conn = psycopg2.connect(
|
||||
"dbname=sysinv user=postgres port=%s" % postgres_port)
|
||||
|
||||
floating_address_id = get_floating_sc_address_id(
|
||||
conn, virtual_system)
|
||||
if not floating_address_id:
|
||||
LOG.info("System controller address ID not found, exiting.")
|
||||
return 0
|
||||
|
||||
registry_central_ip = get_address_by_id(conn, floating_address_id)
|
||||
if not registry_central_ip:
|
||||
LOG.info("System controller address not found, exiting.")
|
||||
return 0
|
||||
|
||||
if virtual_system:
|
||||
registry_local_ip = get_controller_mgmt_address(conn)
|
||||
update_dns_registry(
|
||||
conn, registry_central_ip, registry_local_ip, to_release)
|
||||
else:
|
||||
update_dns_registry(conn, registry_central_ip, None,
|
||||
to_release)
|
||||
|
||||
if not check_dns_resolution(registry_central_ip):
|
||||
return 1
|
||||
|
||||
except Exception as ex:
|
||||
LOG.exception("Error: %s" % ex)
|
||||
print(ex)
|
||||
return 1
|
||||
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def update_dns_registry(conn, registry_central_ip,
|
||||
registry_local_ip=None, to_release=None):
|
||||
try:
|
||||
delete_query = (
|
||||
"DELETE FROM service_parameter "
|
||||
"WHERE service='dns' AND section='host-record' "
|
||||
"AND name IN ('registry.central', 'registry.local');"
|
||||
)
|
||||
db_execute(conn, delete_query)
|
||||
|
||||
created_at = wtypes.datetime.datetime
|
||||
central_uuid = str(uuid.uuid4())
|
||||
|
||||
insert_central_query = (
|
||||
"INSERT INTO service_parameter "
|
||||
"(uuid, service, section, name, value, personality, "
|
||||
"resource, created_at) "
|
||||
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s);"
|
||||
)
|
||||
central_values = (
|
||||
central_uuid, 'dns', 'host-record', 'registry.central',
|
||||
f"registry.central,{registry_central_ip}",
|
||||
None, None, created_at.utcnow()
|
||||
)
|
||||
db_execute(conn, insert_central_query, central_values)
|
||||
|
||||
if registry_local_ip:
|
||||
local_uuid = str(uuid.uuid4())
|
||||
insert_local_query = (
|
||||
"INSERT INTO service_parameter "
|
||||
"(uuid, service, section, name, value, personality, "
|
||||
"resource, created_at) "
|
||||
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s);"
|
||||
)
|
||||
local_values = (
|
||||
local_uuid, 'dns', 'host-record', 'registry.local',
|
||||
f"registry.local,{registry_local_ip}",
|
||||
None, None, created_at.utcnow()
|
||||
)
|
||||
db_execute(conn, insert_local_query, local_values)
|
||||
|
||||
LOG.info("DNS host records for registry inserted successfully.")
|
||||
|
||||
config_dir = f"/opt/platform/config/{to_release}"
|
||||
config_file = os.path.join(config_dir, "dnsmasq.addn_conf")
|
||||
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
LOG.info("Created config directory: %s" % config_dir)
|
||||
|
||||
existing_lines = []
|
||||
if os.path.exists(config_file):
|
||||
with open(config_file, "r") as f:
|
||||
existing_lines = f.readlines()
|
||||
|
||||
updated_lines = []
|
||||
for line in existing_lines:
|
||||
if not line.startswith("host-record=registry.central,") and \
|
||||
not line.startswith("host-record=registry.local,"):
|
||||
updated_lines.append(line.strip())
|
||||
|
||||
updated_lines.append(
|
||||
f"host-record=registry.central,{registry_central_ip}"
|
||||
)
|
||||
if registry_local_ip:
|
||||
updated_lines.append(
|
||||
f"host-record=registry.local,{registry_local_ip}"
|
||||
)
|
||||
|
||||
with open(config_file, "w") as f:
|
||||
for line in updated_lines:
|
||||
f.write(line + "\n")
|
||||
LOG.info("Updated entry in %s: %s" % (config_file, line))
|
||||
|
||||
except Exception as e:
|
||||
LOG.exception("Failed to update DNS records: %s" % e)
|
||||
raise
|
||||
|
||||
|
||||
def db_execute(conn, query, params=None):
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
if params:
|
||||
cursor.execute(query, params)
|
||||
else:
|
||||
cursor.execute(query)
|
||||
conn.commit()
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
LOG.exception("Error executing query: %s" % e)
|
||||
raise
|
||||
|
||||
|
||||
def db_query(conn, query):
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchone()
|
||||
return result[0] if result else None
|
||||
except Exception as e:
|
||||
LOG.exception("Error executing query: %s" % e)
|
||||
raise
|
||||
|
||||
|
||||
def check_virtual_system(client):
|
||||
parameters = client.sysinv.service_parameter.list()
|
||||
|
||||
for parameter in parameters:
|
||||
if (parameter.name ==
|
||||
sysinv_constants.SERVICE_PARAM_NAME_PLAT_CONFIG_VIRTUAL):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_floating_sc_address_id(conn, virtual_system):
|
||||
if virtual_system:
|
||||
query = (
|
||||
"SELECT floating_address_id FROM address_pools "
|
||||
"WHERE name = 'system-controller-subnet';"
|
||||
)
|
||||
else:
|
||||
query = (
|
||||
"SELECT floating_address_id FROM address_pools "
|
||||
"WHERE name = 'system-controller-oam-subnet';"
|
||||
)
|
||||
|
||||
return db_query(conn, query)
|
||||
|
||||
|
||||
def get_controller_mgmt_address(conn):
|
||||
query = "SELECT address FROM addresses WHERE name = 'controller-mgmt';"
|
||||
return db_query(conn, query)
|
||||
|
||||
|
||||
def get_address_by_id(conn, floating_address_id):
|
||||
query = (
|
||||
"SELECT address FROM addresses WHERE id = %s;"
|
||||
% floating_address_id
|
||||
)
|
||||
return db_query(conn, query)
|
||||
|
||||
|
||||
def check_dns_resolution(ip_address):
|
||||
if valid_ipv4(ip_address):
|
||||
record_type = "A"
|
||||
ip_type = "IPv4"
|
||||
elif valid_ipv6(ip_address):
|
||||
record_type = "AAAA"
|
||||
ip_type = "IPv6"
|
||||
else:
|
||||
LOG.error("Invalid IP address: %s" % ip_address)
|
||||
return False
|
||||
|
||||
LOG.info("Checking resolution to registry.central")
|
||||
|
||||
result = subprocess.run(
|
||||
["dig", "registry.central", record_type, "+short"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
universal_newlines=True
|
||||
)
|
||||
|
||||
if result.returncode != 0 or not result.stdout.strip():
|
||||
LOG.error(
|
||||
"Failed to resolve %s address %s to a name associated with "
|
||||
"the domain (registry.central). No valid DNS record found." %
|
||||
(ip_type, ip_address)
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,91 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import logging as LOG
|
||||
import psycopg2
|
||||
import sys
|
||||
|
||||
DEFAULT_POSTGRES_PORT = 5432
|
||||
LOG_FILE = "/var/log/software.log"
|
||||
DB_NAME = "sysinv"
|
||||
DB_USER = "postgres"
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
postgres_port = DEFAULT_POSTGRES_PORT
|
||||
arg = 1
|
||||
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
# optional port parameter for USM upgrade
|
||||
postgres_port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
log_format = ('%(asctime)s: ' + '[%(process)s]: '
|
||||
'%(filename)s(%(lineno)s): %(levelname)s: %(message)s')
|
||||
LOG.basicConfig(filename=LOG_FILE, format=log_format, level=LOG.INFO,
|
||||
datefmt="%FT%T")
|
||||
|
||||
res = 0
|
||||
LOG.info("%s invoked from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action))
|
||||
|
||||
if action == "migrate" and from_release == "24.09":
|
||||
LOG.info("Updating addresses table entries.")
|
||||
|
||||
try:
|
||||
update_address_name_from_db(postgres_port)
|
||||
except Exception as ex:
|
||||
LOG.exception("Error: {}".format(ex))
|
||||
print(ex)
|
||||
res = 1
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def update_address_name_from_db(postgres_port):
|
||||
query = """
|
||||
UPDATE addresses
|
||||
SET name = REGEXP_REPLACE(
|
||||
name, '^system-controller-gateway-ip-', 'controller-gateway-')
|
||||
WHERE name LIKE 'system-controller-gateway-ip-%';
|
||||
"""
|
||||
try:
|
||||
with psycopg2.connect(
|
||||
dbname=DB_NAME,
|
||||
user=DB_USER,
|
||||
port=postgres_port
|
||||
) as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(query)
|
||||
rows_updated = cursor.rowcount
|
||||
conn.commit()
|
||||
|
||||
if rows_updated:
|
||||
LOG.info(
|
||||
"Updated %d entries in addresses table.", rows_updated)
|
||||
else:
|
||||
LOG.info("No entries updated in addresses table.")
|
||||
except Exception as e:
|
||||
LOG.error(f"Failed to update IP addresses in the "
|
||||
f"database: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,50 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2022-2024 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Sometimes docker will be in a bad state.
|
||||
# Check for this and use some recovery logic to get it back to normal.
|
||||
|
||||
# Parameters for recovery logic
|
||||
MAX_ATTEMPTS=5
|
||||
TIME_STEP=6
|
||||
|
||||
# The migration scripts are passed these parameters:
|
||||
NAME=$(basename $0)
|
||||
FROM_RELEASE=$1
|
||||
TO_RELEASE=$2
|
||||
ACTION=$3
|
||||
|
||||
function log {
|
||||
echo "$(date -Iseconds | cut -d'+' -f1): ${NAME}[$$]: INFO: $*" >> "/var/log/software.log" 2>&1
|
||||
}
|
||||
|
||||
# Script start
|
||||
if [[ "${ACTION}" != "activate" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Starting docker health check script from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
|
||||
# Docker is considered in a "bad state" if the service isn't active or
|
||||
# if "/var/lib/docker/tmp" doesn't exist, as it won't be able to download images
|
||||
attempts=0
|
||||
while [ "$(systemctl is-active docker)" != "active" ] || [ ! -d "/var/lib/docker/tmp" ]
|
||||
do
|
||||
attempts=$(( $attempts + 1 ))
|
||||
if [ "$attempts" -gt "$MAX_ATTEMPTS" ]; then
|
||||
log "Could not fix docker service."
|
||||
exit 0
|
||||
fi
|
||||
log "Docker in bad state. Restarting docker service. Attempt: $attempts/$MAX_ATTEMPTS"
|
||||
|
||||
systemctl restart docker
|
||||
|
||||
sleep $TIME_STEP
|
||||
done
|
||||
|
||||
log "Docker service is active and healthy"
|
||||
|
||||
exit 0
|
@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2021-2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script will clear the host config target.
|
||||
# This is required in order to ensure tracking is aligned with config
|
||||
# requests in N+1 release and not due to potential stale configuration
|
||||
# from N release.
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from psycopg2.extras import RealDictCursor
|
||||
from controllerconfig import utils
|
||||
from controllerconfig.common import constants
|
||||
from controllerconfig.common.usm_log import configure_logging
|
||||
|
||||
LOG = logging.getLogger('main_logger')
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
postgres_port = constants.POSTGRESQL_DEFAULT_PORT
|
||||
arg = 1
|
||||
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg] # noqa
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
postgres_port = sys.argv[arg]
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
configure_logging()
|
||||
|
||||
LOG.debug("%s invoked with from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action))
|
||||
|
||||
# This host table data migration will likely be required for each release
|
||||
if action == "migrate":
|
||||
try:
|
||||
reset_config_target(postgres_port)
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
return 1
|
||||
|
||||
|
||||
def reset_config_target(port):
|
||||
|
||||
conn = utils.connect_to_postgresql(port)
|
||||
with conn:
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
cur.execute("update i_host set config_target=NULL",)
|
||||
|
||||
LOG.info("Reset host config_target completed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,113 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script is responsible for updating the software_version
|
||||
# in i_system table during the USM upgrade
|
||||
|
||||
|
||||
import logging
|
||||
import psycopg2
|
||||
import sys
|
||||
|
||||
from controllerconfig.common.usm_log import configure_logging
|
||||
|
||||
LOG = logging.getLogger('main_logger')
|
||||
|
||||
DEFAULT_POSTGRES_PORT = 5432
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None # noqa
|
||||
postgres_port = DEFAULT_POSTGRES_PORT
|
||||
arg = 1
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg] # noqa
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
# optional port parameter for USM upgrade
|
||||
postgres_port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print(f"Invalid option {sys.argv[arg]}.")
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
configure_logging()
|
||||
|
||||
if action in ["activate", "activate-rollback"]:
|
||||
try:
|
||||
# This username/password authentication is required in activate
|
||||
# or rollback actions to connect to the database
|
||||
# For migration, we don't need username/password and host. Peer
|
||||
# authentication is available in the case of migration
|
||||
username, password = get_db_credentials()
|
||||
conn = psycopg2.connect("dbname=sysinv user=%s password=%s \
|
||||
host=localhost port=%s"
|
||||
% (username, password, postgres_port))
|
||||
except Exception as e:
|
||||
LOG.exception(f"Error connecting to database: {e}")
|
||||
return 1
|
||||
try:
|
||||
LOG.info(f"Updating software_version from {from_release} \
|
||||
to {to_release}\n")
|
||||
update_isystem_software_version(conn, to_release)
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
return 1
|
||||
finally:
|
||||
conn.close()
|
||||
return 0
|
||||
|
||||
|
||||
def update_isystem_software_version(conn, new_sw_version):
|
||||
"""
|
||||
This function updates the software_version in isystem table
|
||||
"""
|
||||
|
||||
update_isystem_software_version_query = \
|
||||
f"UPDATE i_system SET software_version='{new_sw_version}';"
|
||||
db_update(conn, update_isystem_software_version_query)
|
||||
LOG.info(f"Updated software_version to {new_sw_version}")
|
||||
|
||||
|
||||
def get_db_credentials():
|
||||
import re
|
||||
import configparser
|
||||
|
||||
configparser = configparser.ConfigParser()
|
||||
configparser.read('/etc/sysinv/sysinv.conf')
|
||||
conn_string = configparser['database']['connection']
|
||||
match = re.match(r'postgresql\+psycopg2://([^:]+):([^@]+)@', conn_string)
|
||||
if match:
|
||||
username = match.group(1)
|
||||
password = match.group(2)
|
||||
return username, password
|
||||
else:
|
||||
raise Exception("Failed to get database credentials from sysinv.conf")
|
||||
|
||||
|
||||
def db_query(conn, query):
|
||||
result = []
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query)
|
||||
for rec in cur:
|
||||
result.append(rec)
|
||||
return result
|
||||
|
||||
|
||||
def db_update(conn, query):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query)
|
||||
conn.commit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,101 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script reconfigure the keystone endpoints using the sysinv
|
||||
# version (not puppet).
|
||||
# Needs to run at the end of the upgrade activation, to reduce the
|
||||
# stabilization time after upgrade is concluded (less reconfigurations).
|
||||
|
||||
import logging
|
||||
import socket
|
||||
import sys
|
||||
from time import sleep
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_context import context as mycontext
|
||||
from six.moves import configparser
|
||||
from sysinv.conductor import rpcapiproxy as conductor_rpcapi
|
||||
|
||||
from controllerconfig.common.usm_log import configure_logging
|
||||
|
||||
LOG = logging.getLogger('main_logger')
|
||||
CONF = cfg.CONF
|
||||
SYSINV_CONFIG_FILE = '/etc/sysinv/sysinv.conf'
|
||||
|
||||
|
||||
def get_conductor_rpc_bind_ip():
|
||||
ini_str = '[DEFAULT]\n' + open(SYSINV_CONFIG_FILE, 'r').read()
|
||||
config_applied = configparser.RawConfigParser()
|
||||
config_applied.read_string(ini_str)
|
||||
|
||||
conductor_bind_ip = None
|
||||
if config_applied.has_option('DEFAULT', 'rpc_zeromq_conductor_bind_ip'):
|
||||
conductor_bind_ip = \
|
||||
config_applied.get('DEFAULT', 'rpc_zeromq_conductor_bind_ip')
|
||||
return conductor_bind_ip
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
arg = 1
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
# optional port parameter for USM upgrade
|
||||
# port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
configure_logging()
|
||||
|
||||
# Activate
|
||||
if action == 'activate':
|
||||
LOG.info("%s invoked with from_release = %s to_release = %s "
|
||||
"action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action))
|
||||
|
||||
# Options of bind ip to the rpc call
|
||||
rpc_ip_options = [get_conductor_rpc_bind_ip(), 'controller.internal']
|
||||
while None in rpc_ip_options:
|
||||
rpc_ip_options.remove(None)
|
||||
|
||||
for index, ip in enumerate(rpc_ip_options):
|
||||
try:
|
||||
CONF.rpc_zeromq_conductor_bind_ip = ip
|
||||
context = mycontext.get_admin_context()
|
||||
rpcapi = conductor_rpcapi.ConductorAPI(
|
||||
topic=conductor_rpcapi.MANAGER_TOPIC)
|
||||
host = rpcapi.get_ihost_by_hostname(
|
||||
context, socket.gethostname())
|
||||
|
||||
LOG.info("Call Conductor to reconfigure keystone endpoints. "
|
||||
"Bind ip: %s." % CONF.rpc_zeromq_conductor_bind_ip)
|
||||
rpcapi.reconfigure_service_endpoints(context, host)
|
||||
except Exception as e:
|
||||
if index == (len(rpc_ip_options) - 1):
|
||||
LOG.error("Error configuring keystone endpoints. "
|
||||
"Please verify logs.")
|
||||
return 1
|
||||
else:
|
||||
LOG.exception(e)
|
||||
LOG.error("Exception ocurred during script execution, "
|
||||
"retrying after 5 seconds.")
|
||||
sleep(5)
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,82 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (c) 2022-2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script install fluxcd controllers in the fluxcd-helm namespace
|
||||
# in kubernetes
|
||||
#
|
||||
# This script can be removed in the release that follows stx7
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from sysinv.common import exception
|
||||
from sysinv.common.retrying import retry
|
||||
from sysinv.common.kubernetes import test_k8s_health
|
||||
|
||||
from controllerconfig.common.usm_log import configure_logging
|
||||
|
||||
LOG = logging.getLogger('main_logger')
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
arg = 1
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
# postgres_port = sys.argv[arg]
|
||||
pass
|
||||
else:
|
||||
print("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
configure_logging()
|
||||
|
||||
if action == 'activate' and from_release >= '21.12':
|
||||
LOG.info("%s invoked with from_release = %s to_release = %s "
|
||||
"action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action))
|
||||
enable_fluxcd_controllers(from_release)
|
||||
|
||||
|
||||
@retry(retry_on_exception=lambda x: isinstance(x, exception.SysinvException),
|
||||
stop_max_attempt_number=3)
|
||||
@test_k8s_health
|
||||
def enable_fluxcd_controllers(from_release):
|
||||
"""Run fluxcd_controllers ansible playbook to enable fluxcd controllers
|
||||
|
||||
"""
|
||||
|
||||
playbooks_root = '/usr/share/ansible/stx-ansible/playbooks'
|
||||
upgrade_script = 'upgrade-fluxcd-controllers.yml'
|
||||
cmd = 'ansible-playbook {}/{} -e "upgrade_activate_from_release={}"'\
|
||||
''.format(playbooks_root, upgrade_script, from_release)
|
||||
|
||||
try:
|
||||
sub = subprocess.Popen(cmd, shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = sub.communicate()
|
||||
if sub.returncode != 0:
|
||||
LOG.error('Command failed:\n %s\n. %s\n%s' % (
|
||||
cmd, stdout.decode('utf-8'), stderr.decode('utf-8')))
|
||||
raise Exception('Cannot install fluxcd controllers')
|
||||
LOG.info('FluxCD controllers enabled. Output: %s' %
|
||||
stdout.decode('utf-8'))
|
||||
except Exception as e:
|
||||
raise exception.SysinvException(
|
||||
f"Error trying to enable fluxcd controllers via {cmd}, reason: {e}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,72 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
NAME=$(basename "$0")
|
||||
FROM_RELEASE=$1
|
||||
TO_RELEASE=$2
|
||||
ACTION=$3
|
||||
SYSCTL_FILE="/etc/sysctl.conf"
|
||||
LOG_FILE="/var/log/software.log"
|
||||
|
||||
function log {
|
||||
echo "$(date -Iseconds | cut -d'+' -f1): ${NAME}[$$]: INFO: $*" >> "$LOG_FILE" 2>&1
|
||||
}
|
||||
|
||||
if [[ "${ACTION}" == "activate" ]]; then
|
||||
log "Ensure CIS Benchmark Standards are met from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
|
||||
# Ensure config is set correctly
|
||||
grep -q "^net.ipv4.conf.default.rp_filter" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^net.ipv4.conf.default.rp_filter.*/net.ipv4.conf.default.rp_filter=1/" "$SYSCTL_FILE" || \
|
||||
echo "net.ipv4.conf.default.rp_filter=1" >> "$SYSCTL_FILE"
|
||||
|
||||
grep -q "^net.ipv4.conf.all.rp_filter" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^net.ipv4.conf.all.rp_filter.*/net.ipv4.conf.all.rp_filter=1/" "$SYSCTL_FILE" || \
|
||||
echo "net.ipv4.conf.all.rp_filter=1" >> "$SYSCTL_FILE"
|
||||
|
||||
grep -q "net.ipv4.tcp_syncookies" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^#*\s*net.ipv4.tcp_syncookies.*/net.ipv4.tcp_syncookies=1/" "$SYSCTL_FILE" || \
|
||||
echo "net.ipv4.tcp_syncookies=1" >> "$SYSCTL_FILE"
|
||||
|
||||
grep -q "net.ipv4.icmp_echo_ignore_broadcasts" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^#*\s*net.ipv4.icmp_echo_ignore_broadcasts.*/net.ipv4.icmp_echo_ignore_broadcasts=1/" "$SYSCTL_FILE" || \
|
||||
echo "net.ipv4.icmp_echo_ignore_broadcasts=1" >> "$SYSCTL_FILE"
|
||||
|
||||
grep -q "net.ipv4.conf.all.accept_source_route" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^#*\s*net.ipv4.conf.all.accept_source_route.*/net.ipv4.conf.all.accept_source_route=0/" "$SYSCTL_FILE" || \
|
||||
echo "net.ipv4.conf.all.accept_source_route=0" >> "$SYSCTL_FILE"
|
||||
|
||||
# Apply changes
|
||||
sysctl --system &>/dev/null
|
||||
log "Applied CIS Benchmark required config"
|
||||
|
||||
elif [[ "${ACTION}" == "activate-rollback" ]]; then
|
||||
log "Rolling back CIS Benchmark changes from release $FROM_RELEASE to $TO_RELEASE"
|
||||
|
||||
# Reverse config
|
||||
grep -q "^net.ipv4.conf.default.rp_filter" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^net.ipv4.conf.default.rp_filter.*/net.ipv4.conf.default.rp_filter=0/" "$SYSCTL_FILE"
|
||||
|
||||
grep -q "^net.ipv4.conf.all.rp_filter" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^net.ipv4.conf.all.rp_filter.*/net.ipv4.conf.all.rp_filter=0/" "$SYSCTL_FILE"
|
||||
|
||||
grep -q "^net.ipv4.tcp_syncookies" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^net.ipv4.tcp_syncookies.*/#net.ipv4.tcp_syncookies=1/" "$SYSCTL_FILE"
|
||||
|
||||
grep -q "^net.ipv4.icmp_echo_ignore_broadcasts" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^net.ipv4.icmp_echo_ignore_broadcasts.*/#net.ipv4.icmp_echo_ignore_broadcasts=1/" "$SYSCTL_FILE"
|
||||
|
||||
grep -q "^net.ipv4.conf.all.accept_source_route" "$SYSCTL_FILE" && \
|
||||
sed -i "s/^net.ipv4.conf.all.accept_source_route.*/#net.ipv4.conf.all.accept_source_route=0/" "$SYSCTL_FILE"
|
||||
|
||||
# Apply changes
|
||||
sysctl --system &>/dev/null
|
||||
log "Rollback applied: Restored previous values"
|
||||
|
||||
else
|
||||
exit 0
|
||||
fi
|
@ -1,55 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This migration script is used to create keystone roles
|
||||
# operator and configurator during upgrade, also deletes
|
||||
# roles when the rollback is executed
|
||||
#
|
||||
|
||||
# The migration scripts are passed these parameters:
|
||||
NAME=$(basename $0)
|
||||
FROM_RELEASE=$1
|
||||
TO_RELEASE=$2
|
||||
ACTION=$3
|
||||
ROLES=("operator" "configurator")
|
||||
|
||||
function log {
|
||||
echo "$(date -Iseconds | cut -d'+' -f1): ${NAME}[$$]: INFO: $*" >> "/var/log/software.log" 2>&1
|
||||
}
|
||||
|
||||
# Only run this script during upgrade-activate and from release 24.09
|
||||
if [[ "$ACTION" == "activate" && "$FROM_RELEASE" == "24.09" ]]; then
|
||||
log "creating keystone roles operator,configurator"
|
||||
for role in "${ROLES[@]}"; do
|
||||
openstack role show $role
|
||||
RC=$?
|
||||
if [ ${RC} == 1 ]; then
|
||||
openstack role create $role
|
||||
RC=$?
|
||||
if [ ${RC} == 0 ]; then
|
||||
log "Successfully added keystone role ${role}"
|
||||
else
|
||||
log "Failed to add keystone role ${role}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
elif [[ "$ACTION" == "activate-rollback" && "$TO_RELEASE" == "24.09" ]]; then
|
||||
for role in "${ROLES[@]}"; do
|
||||
openstack role show $role
|
||||
RC=$?
|
||||
if [ ${RC} == 0 ]; then
|
||||
openstack role delete $role
|
||||
RC=$?
|
||||
if [ ${RC} == 0 ]; then
|
||||
log "Successfully deleted keystone role ${role}"
|
||||
else
|
||||
log "Failed to delete keystone role ${role}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
@ -1,541 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2020-2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# This migration script is used for upgrading platform applications during the
|
||||
# activate stage of a platform upgrade. It will:
|
||||
# - Ignore any new applications that are installed in the To-Release and rely on
|
||||
# any platform-managed application logic to upload/apply it after the upgrade
|
||||
# has completed.
|
||||
# - Attempt to delete and upload any apps that were in the uploaded state in the
|
||||
# From-Release if the version has changed in the To-Release
|
||||
# - Attempt to update any app that was in the applied state in the From-Release
|
||||
# if the version has changed in the To-Release
|
||||
|
||||
NAME=$(basename $0)
|
||||
|
||||
# The migration scripts are passed these parameters:
|
||||
FROM_RELEASE=$1
|
||||
TO_RELEASE=$2
|
||||
ACTION=$3
|
||||
|
||||
if (( $# != 3 && $# != 4 )); then
|
||||
>&2 echo "Error: Missing Arguments!"
|
||||
>&2 echo "Usage: 65-k8s-app-upgrade.sh FROM_RELEASE TO_RELEASE ACTION"
|
||||
>&2 echo "Exiting for manual intervention..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
UPGRADE_IN_PROGRESS_APPS_FILE='/etc/platform/.upgrade_in_progress_apps'
|
||||
|
||||
TIMEOUT=600
|
||||
KUBE_SYSTEM_NAMESPACE="kube-system"
|
||||
CERT_MANAGER_NAMESPACE="cert-manager"
|
||||
|
||||
RECOVER_RESULT_SLEEP=30
|
||||
RECOVER_RESULT_ATTEMPTS=30 # ~15 min to recover app
|
||||
DELETE_RESULT_SLEEP=10
|
||||
DELETE_RESULT_ATTEMPTS=6 # ~1 min to delete app
|
||||
UPLOAD_RESULT_SLEEP=10
|
||||
UPLOAD_RESULT_ATTEMPTS=24 # ~4 min to upload app
|
||||
UPDATE_RESULT_SLEEP=30
|
||||
UPDATE_RESULT_ATTEMPTS=30 # ~15 min to update app
|
||||
COMMAND_RETRY_SLEEP=30
|
||||
COMMAND_RETRY_ATTEMPTS=10 # ~5 min to wait on a retried command.
|
||||
SOFTWARE_LOG_PATH='/var/log/software.log'
|
||||
CRITICAL_APPS='nginx-ingress-controller cert-manager platform-integ-apps'
|
||||
APPS_NOT_TO_UPDATE=''
|
||||
|
||||
TEST_CERT_CM="
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: system-local-ca
|
||||
spec:
|
||||
ca:
|
||||
secretName: system-local-ca
|
||||
status: {}
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: stx-test-cm
|
||||
namespace: cert-manager
|
||||
spec:
|
||||
commonName: stx-test-cm
|
||||
issuerRef:
|
||||
kind: ClusterIssuer
|
||||
name: system-local-ca
|
||||
secretName: stx-test-cm
|
||||
status: {}
|
||||
"
|
||||
|
||||
function log {
|
||||
echo "$(date -Iseconds | cut -d'+' -f1): ${NAME}[$$]: INFO: $*" >> "$SOFTWARE_LOG_PATH" 2>&1
|
||||
}
|
||||
|
||||
function get_api_token {
|
||||
curl -v -X POST "${1}/auth/tokens" \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"auth": {
|
||||
"identity": {
|
||||
"methods": [
|
||||
"password"
|
||||
],
|
||||
"password": {
|
||||
"user": {
|
||||
"domain": {
|
||||
"name": "Default"
|
||||
},
|
||||
"name": "'${2}'",
|
||||
"password": "'${3}'"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scope": {
|
||||
"project": {
|
||||
"domain": {
|
||||
"name": "Default"
|
||||
},
|
||||
"name": "admin"
|
||||
}
|
||||
}
|
||||
}
|
||||
}' 2>&1 | sed -n 's/.*[t|T]oken: \(.*\)/\1/p'
|
||||
}
|
||||
|
||||
function verify_apps_are_not_recovering {
|
||||
# Scrape app names. Skip header and footer.
|
||||
APPS=$(system application-list --nowrap | head -n-1 | tail -n+4 | awk '{print $2}')
|
||||
for a in ${APPS}; do
|
||||
log "Checking application ${a} current state..."
|
||||
|
||||
# If app is being upgraded then ignore
|
||||
if [[ -f $UPGRADE_IN_PROGRESS_APPS_FILE ]] && grep -q $a $UPGRADE_IN_PROGRESS_APPS_FILE; then
|
||||
log "${a} is being upgraded."
|
||||
continue
|
||||
fi
|
||||
|
||||
APP_STATUS=$(system application-show $a --column status --format value)
|
||||
if [[ "${APP_STATUS}" =~ ^(applying|restore-requested)$ ]]; then
|
||||
if [ ${system_type} == 'All-in-one' ] && [ ${system_mode} == 'simplex' ]; then
|
||||
log "$a is in a recovering state: ${APP_STATUS}. Waiting for all applications to be uploaded or applied."
|
||||
return 1
|
||||
else
|
||||
log "$a is in an unexpected state: ${APP_STATUS}. Exiting for manual intervention..."
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
function retry_command {
|
||||
# This command attempts to retry the command provided and waits to see if it
|
||||
# executed sucessfully or failed.
|
||||
|
||||
COMMAND=$1
|
||||
APPLICATION_NAME=$2
|
||||
|
||||
if (( $# != 2 )); then
|
||||
>&2 echo "Error: Missing Arguments!"
|
||||
>&2 echo "Usage: retry_command COMMAND APPLICATION_NAME"
|
||||
>&2 echo "Exiting for manual intervention..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Retrying command: ${COMMAND}"
|
||||
|
||||
system ${COMMAND} ${APPLICATION_NAME}
|
||||
|
||||
# Do an initial sleep before first status check attempt
|
||||
sleep $COMMAND_RETRY_SLEEP
|
||||
|
||||
for tries in $(seq 1 $COMMAND_RETRY_ATTEMPTS); do
|
||||
|
||||
APP_STATUS=$(system application-show ${APPLICATION_NAME} --column status --format value)
|
||||
|
||||
if [[ "${APP_STATUS}" =~ ^(uploaded|applied|removed)$ ]]; then
|
||||
# This is if the command succeeded, break here.
|
||||
log "${APPLICATION_NAME} status is: ${APP_STATUS}. Done!"
|
||||
break
|
||||
elif [[ "${APP_STATUS}" =~ ^(upload-failed|apply-failed|remove-failed)$ ]]; then
|
||||
# The command was retried, but resulted in another failure. Nothing more to be done,
|
||||
# so exit.
|
||||
log "${APPLICATION_NAME} status is: ${APP_STATUS}. The retry has failed. Exiting for manual intervention..."
|
||||
exit 1
|
||||
elif [ $tries == $COMMAND_RETRY_ATTEMPTS ]; then
|
||||
log "Exceeded maximum application ${COMMAND} time of $(date -u -d @"$((COMMAND_RETRY_ATTEMPTS*COMMAND_RETRY_SLEEP))" +"%Mm%Ss"). Execute upgrade-activate again when all applications are uploaded or applied."
|
||||
exit 1
|
||||
fi
|
||||
log "${APPLICATION_NAME} status is: ${APP_STATUS}. Will check again in ${COMMAND_RETRY_SLEEP} seconds."
|
||||
sleep $COMMAND_RETRY_SLEEP
|
||||
done
|
||||
|
||||
log "Retrying command: ${COMMAND} - Succeeded!"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check kubernetes health status.
|
||||
# Exit with status 1 if sysinv-k8s-health command fails
|
||||
function check_k8s_health {
|
||||
local k8s_health
|
||||
sysinv-k8s-health --log-file "${SOFTWARE_LOG_PATH}" check
|
||||
k8s_health=$?
|
||||
|
||||
if [ $k8s_health -eq 1 ]; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# As per cert-manager docs, the webhook server can take some time to come up.
|
||||
# We can ensure the sanity by issuing a certificate as test.
|
||||
function check_cert_manager {
|
||||
log "Issue test certificate to assert cert-manager readyness."
|
||||
RETRIES=60
|
||||
|
||||
check_k8s_health
|
||||
kubectl delete certificate -n cert-manager stx-test-cm --kubeconfig=/etc/kubernetes/admin.conf --ignore-not-found
|
||||
|
||||
apply_failed=1
|
||||
secret_failed=1
|
||||
for retry in $( seq 1 ${RETRIES} ); do
|
||||
if [ ${apply_failed} -ne 0 ]; then
|
||||
log "Apply test certificate CRD..."
|
||||
kubectl apply -f <(echo "$TEST_CERT_CM") --kubeconfig=/etc/kubernetes/admin.conf
|
||||
if [ $? -ne 0 ]; then
|
||||
log "Error applying certificate CRD. Retrying."
|
||||
sleep 3
|
||||
continue
|
||||
fi
|
||||
apply_failed=0
|
||||
fi
|
||||
log "Waiting cert-manager to issue the certificate..."
|
||||
sleep 3
|
||||
kubectl get secret -n cert-manager stx-test-cm --kubeconfig=/etc/kubernetes/admin.conf
|
||||
if [ $? -eq 0 ]; then
|
||||
log "cert-manager is ready to issue certificates."
|
||||
secret_failed=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
check_k8s_health
|
||||
kubectl delete certificate -n cert-manager stx-test-cm --kubeconfig=/etc/kubernetes/admin.conf
|
||||
|
||||
if [ ${secret_failed} -ne 0 ]; then
|
||||
log "Cert-manager is not ready after the allotted time. Check the pod logs."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function check_pod_readiness {
|
||||
# Check the status of nginx-ingress-controller and cert-manager pods
|
||||
|
||||
# Wait for the Nginx Ingress Controller pods to be ready in the background
|
||||
check_k8s_health
|
||||
log "Waiting for Nginx Ingress Controller Pod Status ..."
|
||||
kubectl --kubeconfig=/etc/kubernetes/admin.conf wait --for=condition=ready pod --all=true -n $KUBE_SYSTEM_NAMESPACE -lapp.kubernetes.io/name=ingress-nginx --timeout=${TIMEOUT}s
|
||||
RESULT1=$?
|
||||
|
||||
# Wait for the Cert Manager pods to be ready in the background
|
||||
check_k8s_health
|
||||
log "Waiting for Cert-manager Pod Status ..."
|
||||
kubectl --kubeconfig=/etc/kubernetes/admin.conf wait --for=condition=ready pod --all=true -n $CERT_MANAGER_NAMESPACE -lapp=cert-manager --timeout=${TIMEOUT}s
|
||||
RESULT2=$?
|
||||
|
||||
# Check the results and provide specific message
|
||||
if [ $RESULT1 -eq 0 ] && [ $RESULT2 -eq 0 ]; then
|
||||
log "All required pods for Ingress Nginx Controller and Cert Manager are ready."
|
||||
check_cert_manager
|
||||
elif [ $RESULT1 -ne 0 ] && [ $RESULT2 -eq 0 ]; then
|
||||
log "ERROR: Ingress NGINX pods did not become ready within the timeout period."
|
||||
exit 1
|
||||
elif [ $RESULT1 -eq 0 ] && [ $RESULT2 -ne 0 ]; then
|
||||
log "ERROR: Cert Manager pods did not become ready within the timeout period."
|
||||
exit 1
|
||||
else
|
||||
log "ERROR: Both Ingress Nginx Ingress Controller and Cert Manager pods did not become ready within the timeout period."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function update_in_series {
|
||||
log "App ${EXISTING_APP_NAME} needs to be updated serially"
|
||||
# Wait on the upload, should be quick
|
||||
UPDATED=false
|
||||
for tries in $(seq 1 $UPDATE_RESULT_ATTEMPTS); do
|
||||
UPDATING_APP_INFO=$(system application-show $UPGRADE_APP_NAME --column name --column app_version --column status --format yaml)
|
||||
UPDATING_APP_NAME=$(echo ${UPDATING_APP_INFO} | sed 's/.*name:[[:space:]]\(\S*\).*/\1/')
|
||||
UPDATING_APP_VERSION=$(echo ${UPDATING_APP_INFO} | sed 's/.*app_version:[[:space:]]\(\S*\).*/\1/')
|
||||
UPDATING_APP_STATUS=$(echo ${UPDATING_APP_INFO} | sed 's/.*status:[[:space:]]\(\S*\).*/\1/')
|
||||
|
||||
if [ "${UPDATING_APP_VERSION}" == "${UPGRADE_APP_VERSION}" ] && \
|
||||
[ "${UPDATING_APP_STATUS}" == "applied" ]; then
|
||||
ALARMS=$(fm alarm-list --nowrap --uuid --query "alarm_id=750.005;entity_type_id=k8s_application;entity_instance_id=${UPGRADE_APP_NAME}" | head -n-1 | tail -n+4 | awk '{print $2}')
|
||||
for alarm in ${ALARMS}; do
|
||||
log "$NAME: [Warning] A stale 750.005 Application Update In Progress alarm was found for ${UPGRADE_APP_NAME}. Clearing it (UUID: ${alarm})."
|
||||
fm alarm-delete $alarm --yes
|
||||
done
|
||||
log "$NAME: ${UPGRADE_APP_NAME} has been updated to version ${UPGRADE_APP_VERSION} from version ${EXISTING_APP_VERSION}"
|
||||
UPDATED=true
|
||||
break
|
||||
fi
|
||||
sleep $UPDATE_RESULT_SLEEP
|
||||
done
|
||||
|
||||
if [ $UPDATED == false ] && [ $tries == $UPDATE_RESULT_ATTEMPTS ]; then
|
||||
log "$NAME: ${UPGRADE_APP_NAME}, version ${UPGRADE_APP_VERSION}, was not updated in the alloted time. Exiting for manual intervention..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $tries != $UPDATE_RESULT_ATTEMPTS ] && [ "${UPDATING_APP_VERSION}" == "${EXISTING_APP_VERSION}" ] ; then
|
||||
log "$NAME: ${UPGRADE_APP_NAME}, version ${UPGRADE_APP_VERSION}, update failed and was rolled back. Exiting for manual intervention..."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function update_apps {
|
||||
PATHS_TO_TARBALLS=$1
|
||||
IS_SERIAL_INSTALLATION=$2
|
||||
|
||||
LAST_APP_CHECKED=""
|
||||
# Get the list of applications installed in the new release
|
||||
for fqpn_app in $PATHS_TO_TARBALLS; do
|
||||
# Extract the app name and version from the tarball name: app_name-version.tgz
|
||||
re='^(.*)-([0-9]+\.[0-9]+-[0-9]+).tgz'
|
||||
[[ "$(basename $fqpn_app)" =~ $re ]]
|
||||
UPGRADE_APP_NAME=${BASH_REMATCH[1]}
|
||||
UPGRADE_APP_VERSION=${BASH_REMATCH[2]}
|
||||
log "Found application ${UPGRADE_APP_NAME}, version ${UPGRADE_APP_VERSION} at $fqpn_app"
|
||||
|
||||
# Confirm application is loaded.
|
||||
EXISTING_APP_NAME=$(system application-show $UPGRADE_APP_NAME --column name --format value)
|
||||
if [ -z "${EXISTING_APP_NAME}" ]; then
|
||||
log "${UPGRADE_APP_NAME} is currently not uploaded in the system. skipping..."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if the app name is in the list of apps that should not be updated.
|
||||
if [[ " $APPS_NOT_TO_UPDATE " == *" $UPGRADE_APP_NAME "* ]]; then
|
||||
log "${UPGRADE_APP_NAME} is listed as an app that should not be updated. skipping..."
|
||||
continue
|
||||
fi
|
||||
|
||||
# If the last iteration for the same app was sucessful no further updates are necessary
|
||||
if [ "${LAST_APP_CHECKED}" == "${UPGRADE_APP_NAME}" ] && [[ "${EXISTING_APP_STATUS}" =~ ^(uploaded|applied)$ ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Get the existing application details
|
||||
EXISTING_APP_INFO=$(system application-show $EXISTING_APP_NAME --column app_version --column status --format yaml)
|
||||
EXISTING_APP_VERSION=$(echo ${EXISTING_APP_INFO} | sed 's/.*app_version:[[:space:]]\(\S*\).*/\1/')
|
||||
EXISTING_APP_STATUS=$(echo ${EXISTING_APP_INFO} | sed 's/.*status:[[:space:]]\(\S*\).*/\1/')
|
||||
|
||||
log "$EXISTING_APP_NAME, version $EXISTING_APP_VERSION, is currently in the state: $EXISTING_APP_STATUS"
|
||||
|
||||
if [ "x${UPGRADE_APP_VERSION}" == "x${EXISTING_APP_VERSION}" ]; then
|
||||
# If the app is in uploaded or applied state, then we continue with next iteration.
|
||||
# Else, the code execution proceeds and the script would exit with an unexpected state.
|
||||
if [[ "${EXISTING_APP_STATUS}" =~ ^(uploaded|applied)$ ]]; then
|
||||
log "${UPGRADE_APP_NAME}, version ${EXISTING_APP_VERSION}, is already present. Skipping..."
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# All applications should be in an 'applied' or 'uploaded' state. Any other state is unexpected
|
||||
case "${EXISTING_APP_STATUS}" in
|
||||
|
||||
# States that are upgradable
|
||||
uploaded)
|
||||
check_k8s_health
|
||||
log "Deleting ${EXISTING_APP_NAME}, version ${EXISTING_APP_VERSION}"
|
||||
system application-delete ${EXISTING_APP_NAME} --yes
|
||||
|
||||
# Wait on the delete, should be quick
|
||||
for tries in $(seq 1 $DELETE_RESULT_ATTEMPTS); do
|
||||
EXISTING_APP_STATUS=$(system application-show $EXISTING_APP_NAME --column status --format value)
|
||||
if [ -z "${EXISTING_APP_STATUS}" ]; then
|
||||
log "${EXISTING_APP_NAME} has been deleted."
|
||||
break
|
||||
fi
|
||||
sleep $DELETE_RESULT_SLEEP
|
||||
done
|
||||
|
||||
if [ $tries == $DELETE_RESULT_ATTEMPTS ]; then
|
||||
log "${EXISTING_APP_NAME}, version ${EXISTING_APP_VERSION}, was not deleted in the alloted time. Exiting for manual intervention..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_k8s_health
|
||||
log "Uploading ${UPGRADE_APP_NAME}, version ${UPGRADE_APP_VERSION} from $fqpn_app"
|
||||
system application-upload $fqpn_app
|
||||
;;
|
||||
|
||||
applied)
|
||||
check_k8s_health
|
||||
log "Updating ${EXISTING_APP_NAME}, from version ${EXISTING_APP_VERSION} to version ${UPGRADE_APP_VERSION} from $fqpn_app"
|
||||
system application-update $fqpn_app
|
||||
|
||||
if [ "$IS_SERIAL_INSTALLATION" == "true" ]; then
|
||||
update_in_series
|
||||
fi
|
||||
;;
|
||||
|
||||
upload-failed)
|
||||
check_k8s_health
|
||||
log "${EXISTING_APP_NAME}, version ${EXISTING_APP_VERSION}, upload failed: ${EXISTING_APP_STATUS}. Retrying command..."
|
||||
retry_command "application-upload" "${EXISTING_APP_NAME}"
|
||||
;;
|
||||
|
||||
apply-failed)
|
||||
check_k8s_health
|
||||
log "${EXISTING_APP_NAME}, version ${EXISTING_APP_VERSION}, apply failed: ${EXISTING_APP_STATUS}. Retrying command..."
|
||||
retry_command "application-apply" "${EXISTING_APP_NAME}"
|
||||
;;
|
||||
|
||||
remove-failed)
|
||||
check_k8s_health
|
||||
log "${EXISTING_APP_NAME}, version ${EXISTING_APP_VERSION}, remove failed: ${EXISTING_APP_STATUS}. Retrying command..."
|
||||
retry_command "application-remove --yes" "${EXISTING_APP_NAME}"
|
||||
;;
|
||||
|
||||
# States that are unexpected
|
||||
uploading | applying | removing | restore-requested | updating | recovering)
|
||||
log "${EXISTING_APP_NAME}, version ${EXISTING_APP_VERSION}, is in an unexpected state: ${EXISTING_APP_STATUS}. Exiting for manual intervention..."
|
||||
exit 1
|
||||
;;
|
||||
|
||||
*)
|
||||
log "${EXISTING_APP_NAME}, version ${EXISTING_APP_VERSION}, is in an unknown state: ${EXISTING_APP_STATUS}. Exiting for manual intervention..."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Include app in upgrade in progress file
|
||||
if [[ ! -f $UPGRADE_IN_PROGRESS_APPS_FILE ]] || ! grep -q "${EXISTING_APP_NAME},${EXISTING_APP_VERSION},${UPGRADE_APP_VERSION}" $UPGRADE_IN_PROGRESS_APPS_FILE; then
|
||||
echo "${EXISTING_APP_NAME},${EXISTING_APP_VERSION},${UPGRADE_APP_VERSION}" >> $UPGRADE_IN_PROGRESS_APPS_FILE
|
||||
log "Added ${EXISTING_APP_NAME} to upgrade in progress control file."
|
||||
fi
|
||||
|
||||
LAST_APP_CHECKED=${UPGRADE_APP_NAME}
|
||||
done
|
||||
}
|
||||
|
||||
log "Starting Kubernetes application updates from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
|
||||
if [ "$ACTION" == "activate" ]; then
|
||||
# remove upgrade in progress file
|
||||
[[ -f $UPGRADE_IN_PROGRESS_APPS_FILE ]] && rm -f $UPGRADE_IN_PROGRESS_APPS_FILE
|
||||
|
||||
# move the costly source command in the if branch, so only execute when needed.
|
||||
source /etc/platform/platform.conf
|
||||
|
||||
for tries in $(seq 1 $RECOVER_RESULT_ATTEMPTS); do
|
||||
if verify_apps_are_not_recovering; then
|
||||
break
|
||||
elif [ $tries == $RECOVER_RESULT_ATTEMPTS ]; then
|
||||
log "Exceeded maximum application recovery time of $(date -u -d @"$((RECOVER_RESULT_ATTEMPTS*RECOVER_RESULT_SLEEP))" +"%Mm%Ss"). Execute upgrade-activate again when all applications are uploaded or applied."
|
||||
exit 1
|
||||
fi
|
||||
sleep $RECOVER_RESULT_SLEEP
|
||||
done
|
||||
|
||||
# Get the current k8s version
|
||||
check_k8s_health
|
||||
K8S_VERSIONS=$(system kube-version-list)
|
||||
ACTIVE_K8S_VERSION=$(echo "$K8S_VERSIONS" | grep ' True ' | grep ' active ' | awk -F '|' '{print $2}' | tr -d ' ')
|
||||
|
||||
# Get token
|
||||
TOKEN=$(get_api_token "${OS_AUTH_URL}" "${OS_USERNAME}" "${OS_PASSWORD}")
|
||||
|
||||
# Get list of apps that need to be installed serially due to application dependencies.
|
||||
REORDER_APPS_ENDPOINT="http://controller:6385/v1/reorder_apps/"
|
||||
RESPONSE=$(curl --silent --retry $COMMAND_RETRY_ATTEMPTS --retry-delay $COMMAND_RETRY_SLEEP --write-out "HTTPSTATUS:%{http_code}" -X GET "$REORDER_APPS_ENDPOINT" -H "X-Auth-Token: ${TOKEN}")
|
||||
|
||||
# Capture the HTTP status code
|
||||
HTTP_STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
|
||||
|
||||
if [[ "$HTTP_STATUS" -ne 200 ]]; then
|
||||
log "Unable to get order of apps. Received HTTP status code $HTTP_STATUS."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ALL_SYSTEM_SERIAL_APPLICATION=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS:.*//')
|
||||
|
||||
# Get compatibles tarballs path with current k8s version
|
||||
QUERY_COMPATIBLE_APPS_ENDPOINT="http://controller:6385/v1/query_compatible_apps/?k8s_ver=${ACTIVE_K8S_VERSION}&include_path=true"
|
||||
RESPONSE=$(curl --silent --retry $COMMAND_RETRY_ATTEMPTS --retry-delay $COMMAND_RETRY_SLEEP --write-out "HTTPSTATUS:%{http_code}" -X GET "$QUERY_COMPATIBLE_APPS_ENDPOINT" -H "X-Auth-Token: ${TOKEN}")
|
||||
|
||||
# Capture the HTTP status code
|
||||
HTTP_STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
|
||||
|
||||
if [[ "$HTTP_STATUS" -ne 200 ]]; then
|
||||
log "Unable to obtain compatible app list. Received HTTP status code $HTTP_STATUS."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PATHS_TO_COMPATIBLE_TARBALLS=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS:.*//')
|
||||
|
||||
# Format values
|
||||
ALL_SYSTEM_SERIAL_APPLICATION=$(echo $ALL_SYSTEM_SERIAL_APPLICATION | sed 's/\[//;s/\]//;s/", "/\n/g;s/"//g')
|
||||
PATHS_TO_COMPATIBLE_TARBALLS=$(echo $PATHS_TO_COMPATIBLE_TARBALLS | sed 's/\[//;s/\]//;s/", "/\n/g;s/"//g')
|
||||
|
||||
CRITICAL_APPS_PATHS=""
|
||||
|
||||
# From the list of PATHS_TO_COMPATIBLE_TARBALLS, apps that have priority for installation by the platform are separated.
|
||||
for app in $CRITICAL_APPS; do
|
||||
# Get the first matching path for the app
|
||||
matched_path=$(echo "$PATHS_TO_COMPATIBLE_TARBALLS" | grep -m 1 "/$app-")
|
||||
|
||||
# Add the matched path to MATCHED_PATHS if found
|
||||
if [ -n "$matched_path" ]; then
|
||||
CRITICAL_APPS_PATHS+="$matched_path "
|
||||
# Remove the matched path from PATHS_TO_COMPATIBLE_TARBALLS
|
||||
PATHS_TO_COMPATIBLE_TARBALLS=$(echo "$PATHS_TO_COMPATIBLE_TARBALLS" | grep -v "$matched_path")
|
||||
fi
|
||||
done
|
||||
|
||||
APPS_IN_SERIAL_PATH=''
|
||||
APPS_IN_PARALLEL_PATHS=''
|
||||
|
||||
# Find matches between ALL_SYSTEM_SERIAL_APPLICATION and PATHS_TO_COMPATIBLE_TARBALLS and save
|
||||
# to APPS_IN_SERIAL_PATH
|
||||
for app in $ALL_SYSTEM_SERIAL_APPLICATION; do
|
||||
# Find the corresponding path in PATHS_TO_COMPATIBLE_TARBALLS
|
||||
matched_path=$(echo "$PATHS_TO_COMPATIBLE_TARBALLS" | grep -m 1 "/$app-")
|
||||
|
||||
# If a match is found, append it to APPS_IN_SERIAL_PATH
|
||||
if [ -n "$matched_path" ]; then
|
||||
APPS_IN_SERIAL_PATH="${APPS_IN_SERIAL_PATH}${matched_path} "
|
||||
fi
|
||||
done
|
||||
|
||||
# Find unmatched paths between ALL_SYSTEM_SERIAL_APPLICATION and PATHS_TO_COMPATIBLE_TARBALLS
|
||||
# and save to APPS_IN_PARALLEL_PATHS
|
||||
for path in $PATHS_TO_COMPATIBLE_TARBALLS; do
|
||||
if ! echo -e "$APPS_IN_SERIAL_PATH" | grep -q "$path"; then
|
||||
APPS_IN_PARALLEL_PATHS="${APPS_IN_PARALLEL_PATHS}${path} "
|
||||
fi
|
||||
done
|
||||
|
||||
update_apps "$CRITICAL_APPS_PATHS" "true"
|
||||
check_pod_readiness
|
||||
|
||||
update_apps "$APPS_IN_PARALLEL_PATHS" "false"
|
||||
update_apps "$APPS_IN_SERIAL_PATH" "true"
|
||||
|
||||
log "Completed Kubernetes application updates for release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
else
|
||||
log "No actions required for from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
fi
|
||||
|
||||
|
||||
exit 0
|
@ -1,105 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2023-2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# This migration script is used to wait for apps that were upgraded by
|
||||
# previous application upgrade scripts on the chain. It will:
|
||||
# - Wait for upgraded applications to be either 'applied' or 'uploaded'
|
||||
# with the new version, these applications must be stored earlier during
|
||||
# upgrade-activate process in a file inside /etc/platform/
|
||||
|
||||
NAME=$(basename $0)
|
||||
|
||||
# The migration scripts are passed these parameters:
|
||||
FROM_RELEASE=$1
|
||||
TO_RELEASE=$2
|
||||
ACTION=$3
|
||||
|
||||
CONFIG_PERMDIR="/opt/platform/config/${TO_RELEASE}"
|
||||
UPGRADE_IN_PROGRESS_APPS_FILE='/etc/platform/.upgrade_in_progress_apps'
|
||||
UPDATE_RESULT_SLEEP=30
|
||||
UPDATE_RESULT_ATTEMPTS=45 # ~22.5 min to allow updates to complete.
|
||||
|
||||
function log {
|
||||
echo "$(date -Iseconds | cut -d'+' -f1): ${NAME}[$$]: INFO: $*" >> "/var/log/software.log" 2>&1
|
||||
}
|
||||
|
||||
log "Starting application upgrade watcher script from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
|
||||
if [ "$ACTION" == "activate" ]; then
|
||||
|
||||
# move the costly source command in the if branch, so only execute when needed.
|
||||
source /etc/platform/platform.conf
|
||||
|
||||
if [ ! -f $UPGRADE_IN_PROGRESS_APPS_FILE ]; then
|
||||
log "No file with application upgrade in progress found, skipping script."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Loop over upgraded apps and wait them to become 'applied' or 'uploaded' with the new version
|
||||
APPS_LIST=$(cat $UPGRADE_IN_PROGRESS_APPS_FILE)
|
||||
for tries in $(seq 1 $UPDATE_RESULT_ATTEMPTS); do
|
||||
log "Checking applications status... Retry ${tries} of ${UPDATE_RESULT_ATTEMPTS}"
|
||||
ALL_UPGRADED="true"
|
||||
UPGRADE_IN_PROGRESS_APPS_LIST=""
|
||||
for app in $APPS_LIST; do
|
||||
re='[[:space:]]*(\S*),(\S*),(\S*)[[:space:]]*'
|
||||
[[ $app =~ $re ]]
|
||||
UPGRADE_APP_NAME=${BASH_REMATCH[1]}
|
||||
EXISTING_APP_VERSION=${BASH_REMATCH[2]}
|
||||
UPGRADE_APP_VERSION=${BASH_REMATCH[3]}
|
||||
|
||||
UPDATING_APP_INFO=$(system application-show $UPGRADE_APP_NAME --column name --column app_version --column status --format yaml)
|
||||
UPDATING_APP_NAME=$(echo ${UPDATING_APP_INFO} | sed 's/.*name:[[:space:]]\(\S*\).*/\1/')
|
||||
UPDATING_APP_VERSION=$(echo ${UPDATING_APP_INFO} | sed 's/.*app_version:[[:space:]]\(\S*\).*/\1/')
|
||||
UPDATING_APP_STATUS=$(echo ${UPDATING_APP_INFO} | sed 's/.*status:[[:space:]]\(\S*\).*/\1/')
|
||||
|
||||
if [ "${UPDATING_APP_NAME}" == "${UPGRADE_APP_NAME}" ] && \
|
||||
[ "${UPDATING_APP_VERSION}" == "${UPGRADE_APP_VERSION}" ]; then
|
||||
case "${UPDATING_APP_STATUS}" in
|
||||
"applied"|"uploaded")
|
||||
ALARMS=$(fm alarm-list --nowrap --uuid --query "alarm_id=750.005;entity_type_id=k8s_application;entity_instance_id=${UPGRADE_APP_NAME}" | head -n-1 | tail -n+4 | awk '{print $2}')
|
||||
for alarm in ${ALARMS}; do
|
||||
log "WARN: A stale 750.005 Application Update In Progress alarm was found for ${UPGRADE_APP_NAME}. Clearing it (UUID: ${alarm})."
|
||||
fm alarm-delete $alarm --yes
|
||||
done
|
||||
log "${UPGRADE_APP_NAME} has been updated to version ${UPGRADE_APP_VERSION} from version ${EXISTING_APP_VERSION}"
|
||||
;;
|
||||
*)
|
||||
log "${UPGRADE_APP_NAME} update in progress to version ${UPGRADE_APP_VERSION} from version ${EXISTING_APP_VERSION}"
|
||||
UPGRADE_IN_PROGRESS_APPS_LIST="${app} ${UPGRADE_IN_PROGRESS_APPS_LIST}"
|
||||
ALL_UPGRADED="false"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
log "WARN: ${UPGRADE_APP_NAME} is on '${UPDATING_APP_STATUS}' state but the version is not updated to ${UPGRADE_APP_VERSION} from version ${EXISTING_APP_VERSION}"
|
||||
UPGRADE_IN_PROGRESS_APPS_LIST="${app} ${UPGRADE_IN_PROGRESS_APPS_LIST}"
|
||||
ALL_UPGRADED="false"
|
||||
fi
|
||||
done
|
||||
|
||||
# Exit loop if all applications are upgraded
|
||||
[[ $ALL_UPGRADED == "true" ]] && break
|
||||
|
||||
# Next iteration will check only apps which upgrade is in progress
|
||||
APPS_LIST=$UPGRADE_IN_PROGRESS_APPS_LIST
|
||||
|
||||
sleep $UPDATE_RESULT_SLEEP
|
||||
done
|
||||
|
||||
if [ $tries == $UPDATE_RESULT_ATTEMPTS ]; then
|
||||
log "One or more apps (${APPS_LIST// /, }) were not updated in the alloted time. Exiting for manual intervention..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# remove upgrade in progress file
|
||||
log "Removing temporary file: $UPGRADE_IN_PROGRESS_APPS_FILE"
|
||||
[[ -f $UPGRADE_IN_PROGRESS_APPS_FILE ]] && rm -f $UPGRADE_IN_PROGRESS_APPS_FILE
|
||||
|
||||
log "Completed application upgrade watcher script from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
else
|
||||
log "No actions required for from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
fi
|
@ -1,155 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2022-2024 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# This script is used to resize the platform (and backup, consequently) filesystems
|
||||
# on System Controller DC, so that to allow an increased parallelism on subclouds
|
||||
# deployment (100+ deployments in parallel). This script will:
|
||||
# - Check if deployment is System Controller DC from distributed_cloud_role variable
|
||||
# sourced from /etc/platform/platform.conf
|
||||
# - Check if platform filesystem needs to be resized (i.e. if less than 20GB in size)
|
||||
# and skip the execution if not
|
||||
# - Check if there is enough space on cgts-vg to resize on both controllers
|
||||
# - Resize backup filesystem on each controller and check if resized successfully
|
||||
# - Resize platform controllerfs and check if resized successfully
|
||||
# - NOTE: this script has to be idempotent and reentrant, since upgrade-activate can
|
||||
# be called multiple times during the upgrade
|
||||
# - NOTE: this script must not fail the upgrade if there is not enough disk space to
|
||||
# resize, and only have to warn the user about the limitation
|
||||
|
||||
NAME=$(basename $0)
|
||||
|
||||
# The migration scripts are passed these parameters:
|
||||
FROM_RELEASE=$1
|
||||
TO_RELEASE=$2
|
||||
ACTION=$3
|
||||
|
||||
EXPANDED_PLATFORM_SIZE=20
|
||||
NODE_LIST=(controller-0 controller-1)
|
||||
RESIZE_SLEEP_TIME=90
|
||||
RESIZE_CHECK_MAX_RETRIES=5
|
||||
|
||||
source /etc/platform/platform.conf
|
||||
|
||||
function log {
|
||||
echo "$(date -Iseconds | cut -d'+' -f1): ${NAME}[$$]: INFO: $*" >> "/var/log/software.log" 2>&1
|
||||
}
|
||||
|
||||
function verify_fs_need_resizing {
|
||||
_PLATFORM_SIZE=$(
|
||||
system controllerfs-list --column name --column size --column state | grep platform | awk '{ print $4; }'
|
||||
)
|
||||
|
||||
echo $_PLATFORM_SIZE # return value so that it can be assigned to variable
|
||||
if [[ $_PLATFORM_SIZE -ge $EXPANDED_PLATFORM_SIZE ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function verify_space_to_resize {
|
||||
_PLATFORM_SIZE=$1
|
||||
_HOSTNAME=$2
|
||||
|
||||
_AVAILABLE_DISK_SIZE=$(system host-lvg-list $_HOSTNAME | grep cgts-vg | awk '{ print $12; }')
|
||||
_INCREASE_DISK_SIZE=$(echo "$EXPANDED_PLATFORM_SIZE - $_PLATFORM_SIZE" | bc)
|
||||
_TOTAL_INCREASE_DISK_SIZE=$(echo "2 * $_INCREASE_DISK_SIZE" | bc) # need to resize platform and backup
|
||||
log "[$_HOSTNAME] Available cgts-vg space: ${_AVAILABLE_DISK_SIZE}G, need ${_TOTAL_INCREASE_DISK_SIZE}G to resize."
|
||||
|
||||
echo $_INCREASE_DISK_SIZE # return value so that it can be assigned to variable
|
||||
return $(echo "! $_AVAILABLE_DISK_SIZE >= $_TOTAL_INCREASE_DISK_SIZE" | bc)
|
||||
}
|
||||
|
||||
function resize_backup_filesystem {
|
||||
_INCREASE_DISK_SIZE=$1
|
||||
_HOSTNAME=$2
|
||||
|
||||
_BACKUP_SIZE=$(system host-fs-list $_HOSTNAME | grep backup | awk '{ print $6; }')
|
||||
_EXPANDED_BACKUP_SIZE=$(echo "$_BACKUP_SIZE + $_INCREASE_DISK_SIZE" | bc)
|
||||
log "[$_HOSTNAME] Current backup size is ${_BACKUP_SIZE}G, new size will be ${_EXPANDED_BACKUP_SIZE}G."
|
||||
system host-fs-modify $_HOSTNAME backup=$_EXPANDED_BACKUP_SIZE
|
||||
sleep 5
|
||||
|
||||
_BACKUP_SIZE=$(system host-fs-list $_HOSTNAME | grep backup | awk '{ print $6; }')
|
||||
return $(echo "! $_BACKUP_SIZE == $_EXPANDED_BACKUP_SIZE" | bc)
|
||||
}
|
||||
|
||||
function resize_platform_controllerfs {
|
||||
_PLATFORM_SIZE=$1
|
||||
log "Current platform size is ${_PLATFORM_SIZE}G, new size will be ${EXPANDED_PLATFORM_SIZE}G."
|
||||
system controllerfs-modify platform=$EXPANDED_PLATFORM_SIZE
|
||||
|
||||
for RETRY in $(seq $RESIZE_CHECK_MAX_RETRIES); do
|
||||
log "Retry $RETRY of $RESIZE_CHECK_MAX_RETRIES, checking if platform filesystem is resized and available..."
|
||||
OUTPUT=$(system controllerfs-list --column name --column size --column state | grep platform)
|
||||
_CURRENT_PLATFORM_SIZE=$(echo $OUTPUT | awk '{ print $4; }')
|
||||
_CURRENT_PLATFORM_STATE=$(echo $OUTPUT | awk '{ print $6; }')
|
||||
log "Current platform fs size/state: ${_CURRENT_PLATFORM_SIZE}/${_CURRENT_PLATFORM_STATE}"
|
||||
if [[ ($_CURRENT_PLATFORM_SIZE -eq $EXPANDED_PLATFORM_SIZE) && ($_CURRENT_PLATFORM_STATE == "available") ]]; then
|
||||
return 0
|
||||
fi
|
||||
# if current size is less than the expanded size, retry the resize command
|
||||
if [[ $_CURRENT_PLATFORM_SIZE -lt $EXPANDED_PLATFORM_SIZE ]]; then
|
||||
log "Current platform size is less than ${EXPANDED_PLATFORM_SIZE}G, retrying resize command..."
|
||||
system controllerfs-modify platform=$EXPANDED_PLATFORM_SIZE
|
||||
fi
|
||||
sleep $RESIZE_SLEEP_TIME
|
||||
done
|
||||
|
||||
if [[ $_CURRENT_PLATFORM_SIZE -eq $EXPANDED_PLATFORM_SIZE ]]; then
|
||||
log "[WARNING] platform fs is resized but not yet in available state."
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Script start
|
||||
log "Starting filesystems resize on DC System Controller for increased parallel subcloud deployment, from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
|
||||
if [[ "$ACTION" == "activate" ]]; then
|
||||
if [[ $distributed_cloud_role == "systemcontroller" ]]; then
|
||||
log "Verifying if filesystems need resizing..."
|
||||
if ! PLATFORM_SIZE=$(verify_fs_need_resizing); then
|
||||
log "No need to resize, platform filesystem has been resized already."
|
||||
exit 0
|
||||
fi
|
||||
log "Platform filesystem needs resizing, current size is ${PLATFORM_SIZE}G, ideal size is ${EXPANDED_PLATFORM_SIZE}G."
|
||||
|
||||
log "Verifying if there is enough available space to resize..."
|
||||
for NODE in "${NODE_LIST[@]}"; do
|
||||
if ! INCREASE_DISK_SIZE=$(verify_space_to_resize $PLATFORM_SIZE $NODE); then
|
||||
log "Not enough space in cgts-vg on $NODE to resize, parallel subcloud deployment will be limited. Resize operations will be skipped."
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
log "LVG cgts-vg has enough space for resizing, continuing with resize operations..."
|
||||
|
||||
log "Trying to resize host-fs backup for both controllers..."
|
||||
for NODE in "${NODE_LIST[@]}"; do
|
||||
if ! resize_backup_filesystem $INCREASE_DISK_SIZE $NODE; then
|
||||
log "Failed while resizing backup fs on $NODE, resize operation aborted."
|
||||
exit 0
|
||||
fi
|
||||
log "Successfully resized backup filesystem on $NODE."
|
||||
done
|
||||
|
||||
log "Trying to resize controllerfs platform filesystem..."
|
||||
if ! resize_platform_controllerfs $PLATFORM_SIZE; then
|
||||
log "Failed while resizing controllerfs platform filesystem, resize operation aborted."
|
||||
exit 0
|
||||
fi
|
||||
log "Successfully resized controllerfs platform filesystem."
|
||||
else
|
||||
log "Not a DC System Controller deployment. No filesystem resize needed."
|
||||
fi
|
||||
log "Filesystems resizing for DC System Controller finished successfully, from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
elif [[ "$ACTION" == "activate-rollback" ]]; then
|
||||
log "The $ACTION action is not reversible for this script."
|
||||
else
|
||||
log "No actions required for from release $FROM_RELEASE to $TO_RELEASE with action $ACTION"
|
||||
fi
|
||||
|
||||
exit 0
|
@ -1,332 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
# This script performs database updation and rollback operations
|
||||
# for the `interfaces` table in the `sysinv` PostgreSQL database,
|
||||
# specifically targeting VF interfaces with PCI SR-IOV class.
|
||||
|
||||
|
||||
import sys
|
||||
import psycopg2
|
||||
import logging as LOG
|
||||
from psycopg2 import sql
|
||||
import json
|
||||
import re
|
||||
import configparser
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
DB_NAME = "sysinv"
|
||||
DB_HOST = "localhost"
|
||||
TABLE_NAME = "interfaces"
|
||||
MAX_TX_RATE = "max_tx_rate"
|
||||
MAX_RX_RATE = "max_rx_rate"
|
||||
IFCAPABILITIES = "ifcapabilities"
|
||||
VF_TYPE = "vf"
|
||||
PCI_CLASS = "pci-sriov"
|
||||
DEFAULT_POSTGRES_PORT = "5432"
|
||||
|
||||
LOG.basicConfig(
|
||||
filename="/var/log/software.log",
|
||||
format='%(asctime)s: [%(process)s]: %(filename)s(%(lineno)s): '
|
||||
'%(levelname)s: %(message)s',
|
||||
level=LOG.INFO,
|
||||
datefmt="%FT%T"
|
||||
)
|
||||
|
||||
|
||||
def get_db_credentials():
|
||||
""" Retrieve DB credentials from sysinv.conf """
|
||||
try:
|
||||
config = configparser.ConfigParser()
|
||||
config.read('/etc/sysinv/sysinv.conf')
|
||||
|
||||
conn_string = config['database']['connection']
|
||||
match = re.match(r'postgresql\+psycopg2://([^:]+):([^@]+)@',
|
||||
conn_string)
|
||||
|
||||
if match:
|
||||
username = match.group(1)
|
||||
password = match.group(2)
|
||||
return username, password
|
||||
else:
|
||||
raise Exception("Failed to parse DB credentials from sysinv.conf")
|
||||
except Exception as e:
|
||||
LOG.error(f"Error getting DB credentials: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def connect_to_db(port):
|
||||
""" Establish DB connection """
|
||||
username, password = get_db_credentials()
|
||||
|
||||
try:
|
||||
conn = psycopg2.connect(
|
||||
dbname=DB_NAME,
|
||||
user=username,
|
||||
password=password,
|
||||
host=DB_HOST,
|
||||
port=port
|
||||
)
|
||||
return conn
|
||||
except Exception as e:
|
||||
LOG.error(f"Database connection failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def db_query(conn, query, params=()):
|
||||
""" Execute SELECT query and return results """
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query, params)
|
||||
return cur.fetchall()
|
||||
|
||||
|
||||
def db_update(conn, query, params=(), autocommit=True):
|
||||
""" Execute UPDATE query """
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(query, params)
|
||||
if autocommit:
|
||||
conn.commit()
|
||||
|
||||
|
||||
def columns_exist(conn):
|
||||
""" Verify required columns exist in the table """
|
||||
query = f"""
|
||||
SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = '{TABLE_NAME}'
|
||||
AND column_name IN ('{MAX_TX_RATE}', '{MAX_RX_RATE}',
|
||||
'{IFCAPABILITIES}');
|
||||
"""
|
||||
|
||||
cols = db_query(conn, query)
|
||||
existing_cols = {col[0] for col in cols}
|
||||
|
||||
if {MAX_TX_RATE, MAX_RX_RATE, IFCAPABILITIES}.issubset(existing_cols):
|
||||
return True
|
||||
else:
|
||||
missing_cols = (
|
||||
{MAX_TX_RATE, MAX_RX_RATE, IFCAPABILITIES} - existing_cols
|
||||
)
|
||||
LOG.error(f"Missing columns: {', '.join(missing_cols)}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def update_data(conn):
|
||||
LOG.info("Starting data updation...")
|
||||
|
||||
select_query = sql.SQL(f"""
|
||||
SELECT id, uuid, {IFCAPABILITIES}
|
||||
FROM {TABLE_NAME}
|
||||
WHERE iftype = %s AND ifclass = %s;
|
||||
""")
|
||||
|
||||
vf_interfaces = []
|
||||
|
||||
vf_interfaces = db_query(
|
||||
conn, select_query, (VF_TYPE, PCI_CLASS)
|
||||
)
|
||||
LOG.info(f"Found {len(vf_interfaces)} VF interfaces to update.")
|
||||
if len(vf_interfaces) == 0:
|
||||
LOG.info("No VF interfaces found to update. No changes required")
|
||||
return
|
||||
|
||||
updated = False
|
||||
|
||||
for iface_id, iface_uuid, ifcapabilities in vf_interfaces:
|
||||
if ifcapabilities:
|
||||
try:
|
||||
capabilities_dict = json.loads(ifcapabilities)
|
||||
except(json.JSONDecodeError, TypeError) as e:
|
||||
raise ValueError(
|
||||
f"Malformed ifcapabilities for UUID {iface_uuid}: {e}"
|
||||
)
|
||||
|
||||
tx_rate = capabilities_dict.get("max_tx_rate", None)
|
||||
|
||||
if "max_tx_rate" in capabilities_dict:
|
||||
del capabilities_dict["max_tx_rate"]
|
||||
|
||||
cleaned_ifcapabilities = json.dumps(capabilities_dict) if \
|
||||
capabilities_dict else None
|
||||
|
||||
# Only update the database if either tx_rate or
|
||||
# cleaned_ifcapabilities has a value
|
||||
if tx_rate is not None or cleaned_ifcapabilities is not None:
|
||||
update_query = sql.SQL(f"""
|
||||
UPDATE {TABLE_NAME}
|
||||
SET {MAX_TX_RATE} = %s, {IFCAPABILITIES} = %s
|
||||
WHERE id = %s;
|
||||
""")
|
||||
|
||||
db_update(
|
||||
conn,
|
||||
update_query,
|
||||
(tx_rate, cleaned_ifcapabilities, iface_id),
|
||||
autocommit=False
|
||||
)
|
||||
updated = True
|
||||
|
||||
LOG.info(f"Updated {TABLE_NAME} for UUID: {iface_uuid} "
|
||||
f"with max_tx_rate: {tx_rate}")
|
||||
|
||||
if updated:
|
||||
conn.commit()
|
||||
LOG.info("All applicable records updated successfully.")
|
||||
else:
|
||||
LOG.info("No changes were made to the database.")
|
||||
|
||||
|
||||
def rollback_data(conn):
|
||||
"""Rollback migration by moving data back to ifcapabilities"""
|
||||
LOG.info("Starting data rollback...")
|
||||
|
||||
select_query = sql.SQL(f"""
|
||||
SELECT id, uuid, {MAX_TX_RATE}, {IFCAPABILITIES}
|
||||
FROM {TABLE_NAME}
|
||||
WHERE iftype = %s AND ifclass = %s;
|
||||
""")
|
||||
|
||||
vf_interfaces = []
|
||||
|
||||
vf_interfaces = db_query(
|
||||
conn, select_query, (VF_TYPE, PCI_CLASS)
|
||||
)
|
||||
LOG.info(f"Found {len(vf_interfaces)} VF interfaces to rollback.")
|
||||
if len(vf_interfaces) == 0:
|
||||
LOG.info("No VF interfaces found to rollback. No changes required")
|
||||
return
|
||||
|
||||
updated = False
|
||||
|
||||
for iface_id, iface_uuid, max_tx_rate, ifcapabilities in vf_interfaces:
|
||||
capabilities = {}
|
||||
|
||||
if max_tx_rate is not None:
|
||||
capabilities["max_tx_rate"] = max_tx_rate
|
||||
|
||||
if ifcapabilities:
|
||||
try:
|
||||
existing = json.loads(ifcapabilities)
|
||||
capabilities.update(existing)
|
||||
except (json.JSONDecodeError, TypeError) as e:
|
||||
raise ValueError(
|
||||
f"Malformed ifcapabilities for UUID {iface_uuid}: {e}"
|
||||
)
|
||||
|
||||
if not capabilities:
|
||||
continue
|
||||
|
||||
new_ifcap = json.dumps(capabilities) if capabilities else None
|
||||
|
||||
if new_ifcap or max_tx_rate is not None:
|
||||
update_query = sql.SQL(f"""
|
||||
UPDATE {TABLE_NAME}
|
||||
SET {IFCAPABILITIES} = %s, {MAX_TX_RATE} = NULL
|
||||
WHERE id = %s;
|
||||
""")
|
||||
|
||||
db_update(
|
||||
conn, update_query, (new_ifcap, iface_id), autocommit=False
|
||||
)
|
||||
updated = True
|
||||
|
||||
LOG.info(
|
||||
f"Rolled back {TABLE_NAME} for UUID: {iface_uuid} "
|
||||
f"with ifcapabilities: {new_ifcap}"
|
||||
)
|
||||
|
||||
if updated:
|
||||
conn.commit()
|
||||
LOG.info("All applicable records rolled back successfully.")
|
||||
else:
|
||||
LOG.info("No changes were made to the database.")
|
||||
|
||||
|
||||
def patch_felix_configuration():
|
||||
"""Ensure FelixConfiguration chainInsertMode is set to Append."""
|
||||
LOG.info("Patching FelixConfiguration to Append...")
|
||||
|
||||
cmd = [
|
||||
"kubectl", "--kubeconfig=/etc/kubernetes/admin.conf",
|
||||
"patch", "felixconfiguration", "default", "--type=merge",
|
||||
"-p", '{"spec":{"chainInsertMode":"Append"}}'
|
||||
]
|
||||
|
||||
retries, delay = 3, 5
|
||||
timeout = 15
|
||||
|
||||
for attempt in range(retries):
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
timeout=timeout
|
||||
)
|
||||
LOG.info(f"Patch applied successfully: {result.stdout}")
|
||||
return
|
||||
except subprocess.TimeoutExpired:
|
||||
LOG.warning(f"Attempt {attempt + 1} timed out after {timeout}s.")
|
||||
except subprocess.CalledProcessError as e:
|
||||
LOG.warning(f"Attempt {attempt + 1} failed: {e.stderr}")
|
||||
|
||||
if attempt < retries - 1:
|
||||
time.sleep(delay)
|
||||
else:
|
||||
LOG.error("FelixConfiguration patch failed after retries.")
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None
|
||||
postgres_port = DEFAULT_POSTGRES_PORT
|
||||
|
||||
arg = 1
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg]
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
elif arg == 4:
|
||||
postgres_port = sys.argv[arg]
|
||||
else:
|
||||
LOG.error(f"Invalid option {sys.argv[arg]}.")
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
if action not in ["activate", "activate-rollback"]:
|
||||
LOG.warning(f"Action '{action}' is not valid. Skipping...")
|
||||
return 0
|
||||
|
||||
try:
|
||||
conn = connect_to_db(postgres_port)
|
||||
|
||||
columns_exist(conn)
|
||||
|
||||
if to_release == "25.09" and action == "activate":
|
||||
update_data(conn)
|
||||
patch_felix_configuration()
|
||||
elif from_release == "25.09" and action == "activate-rollback":
|
||||
rollback_data(conn)
|
||||
else:
|
||||
LOG.error(f"Unknown action: {action}")
|
||||
return 1
|
||||
|
||||
except Exception as e:
|
||||
LOG.error(f"Exception during {action}: {e}", exc_info=True)
|
||||
return 1
|
||||
finally:
|
||||
if 'conn' in locals():
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,6 +1,4 @@
|
||||
usr/local/share/upgrade.d
|
||||
etc/init.d
|
||||
etc/goenabled.d
|
||||
usr/sbin
|
||||
lib/systemd/system
|
||||
|
||||
|
@ -2,4 +2,3 @@ usr/bin/*
|
||||
etc/goenabled.d/*
|
||||
usr/lib/python*/dist-packages/*
|
||||
etc/init.d/*
|
||||
usr/local/share/upgrade.d/*
|
||||
|
@ -20,9 +20,6 @@ override_dh_install:
|
||||
install -p -D -m 700 scripts/config_goenabled_check.sh $(ROOT)/etc/goenabled.d/config_goenabled_check.sh.controller
|
||||
install -d -m 755 $(ROOT)/etc/init.d
|
||||
install -p -D -m 755 scripts/controller_config $(ROOT)/etc/init.d/controller_config
|
||||
install -d -m 755 $(ROOT)/usr/local/share/upgrade.d
|
||||
install -p -D -m 755 upgrade-scripts/* $(ROOT)/usr/local/share/upgrade.d
|
||||
install -d -m 755 $(ROOT)/etc/update.d
|
||||
install -d -m 755 $(ROOT)/lib/systemd/system
|
||||
dh_install
|
||||
|
||||
@ -43,5 +40,3 @@ override_dh_installsystemd:
|
||||
|
||||
override_dh_python3:
|
||||
dh_python3 --shebang=/usr/bin/python3
|
||||
|
||||
override_dh_usrlocal:
|
||||
|
Reference in New Issue
Block a user