
Add a patch activate script to restart the software-controller and VIM services if a restart flag is detected in the system. Also, add a fix to detect this flag in 24.09.300 in case the patch 24.09.400 is removed. Test-Plan SX/DX: PASS: Apply an inservice patch with these scripts and check if the service was restarted PASS: Remove the inservice patch with these changes with success Closes-Bug: 2122607 Change-Id: I80c0ac47b48ed1b32b0a91f75ff77613c07eede1 Signed-off-by: Lindley Vieira <lindley.vieira@windriver.com>
197 lines
6.4 KiB
Python
197 lines
6.4 KiB
Python
#!/usr/bin/env python
|
|
# Copyright (c) 2025 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# This script checks if there is a software-controller restart flag,
|
|
# if it exists:
|
|
# - Create a flag detected by software-controller on startup
|
|
# and set its state to activate-done
|
|
# - Restarts the software-controller-daemon
|
|
#
|
|
|
|
import logging
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
from software.states import DEPLOY_STATES
|
|
from software.utilities.update_deploy_state import update_deploy_state
|
|
from software.utilities.utils import configure_logging
|
|
|
|
LOG = logging.getLogger('main_logger')
|
|
|
|
RESTART_SERVICES_FLAG = "/run/software/.activate.restart-services.flag"
|
|
ACTIVATE_DONE_FLAG = "/run/software/.activate-done.flag"
|
|
|
|
|
|
def apply_fix_to_set_activate_done_state():
|
|
"""
|
|
This is a live patch-back of a function to report the activate-done state after
|
|
the software-controller service is restarted. Only applies when it is removing
|
|
the patch 24.09.400, when applying, it will stop and log 'Already patched".
|
|
"""
|
|
LOG.info("Applying activate done fix in software_controller.py")
|
|
|
|
FILE = "/ostree/1/usr/lib/python3/dist-packages/software/software_controller.py"
|
|
LINE_REGEX = r'^\s*self\.register_deploy_state_change_listeners\(\)\s*$'
|
|
TO_ADD_LINE = "_detect_flag_and_set_to_activate_done"
|
|
|
|
try:
|
|
with open(FILE, "r", encoding="utf-8") as f:
|
|
original_content = f.read()
|
|
except FileNotFoundError:
|
|
LOG.error(f"Error: File not found: {FILE}")
|
|
return
|
|
|
|
# Idempotency check: if function reference already present, assume patched
|
|
if TO_ADD_LINE in original_content:
|
|
LOG.info("Already patched")
|
|
return
|
|
|
|
# Ensure target line exists
|
|
line_re = re.compile(LINE_REGEX)
|
|
lines = original_content.splitlines()
|
|
match_index = None
|
|
for i, line in enumerate(lines):
|
|
if line_re.match(line):
|
|
match_index = i
|
|
break
|
|
|
|
if match_index is None:
|
|
LOG.error("Error: Could not find line to replace")
|
|
return
|
|
|
|
# Build replacement block (mirrors the sed-replaced text and indentation)
|
|
replacement_block = (
|
|
" self.register_deploy_state_change_listeners()\n"
|
|
"\n"
|
|
" self._detect_flag_and_set_to_activate_done()\n"
|
|
"\n"
|
|
" def _detect_flag_and_set_to_activate_done(self):\n"
|
|
" ACTIVATE_DONE_FLAG = \"/run/software/.activate-done.flag\"\n"
|
|
"\n"
|
|
" if os.path.isfile(ACTIVATE_DONE_FLAG):\n"
|
|
" os.remove(ACTIVATE_DONE_FLAG)\n"
|
|
" deploy_state = DeployState.get_instance()\n"
|
|
" deploy_state.activate_done()"
|
|
)
|
|
|
|
# Prepare new content, replacing only the first match
|
|
new_lines = lines[:match_index] + [replacement_block] + lines[match_index + 1:]
|
|
new_content = "\n".join(new_lines)
|
|
|
|
# Backup before writing
|
|
backup_path = f"{FILE}.bak"
|
|
try:
|
|
shutil.copy2(FILE, backup_path)
|
|
except Exception as e:
|
|
LOG.error(f"Error: Unable to create backup at {backup_path}: {e}")
|
|
return
|
|
|
|
# Write patched content
|
|
try:
|
|
with open(FILE, "w", encoding="utf-8", newline="\n") as f:
|
|
f.write(new_content)
|
|
except Exception as e:
|
|
LOG.error(f"Error: Failed to write patched file: {e}")
|
|
# Attempt restore if write failed after backup
|
|
try:
|
|
shutil.move(backup_path, FILE)
|
|
except Exception as e2:
|
|
LOG.error(f"Error: Failed to restore backup: {e2}")
|
|
return
|
|
|
|
# Post-check: confirm the function reference now exists
|
|
try:
|
|
with open(FILE, "r", encoding="utf-8") as f:
|
|
after = f.read()
|
|
except Exception as e:
|
|
LOG.error(f"Error: Failed to read back file for verification: {e}")
|
|
# Attempt restore
|
|
try:
|
|
shutil.move(backup_path, FILE)
|
|
except Exception as e2:
|
|
LOG.error(f"Error: Failed to restore backup: {e2}")
|
|
return
|
|
|
|
if TO_ADD_LINE not in after:
|
|
LOG.error("Error: Patch did not apply correctly. Restoring backup.")
|
|
try:
|
|
shutil.move(backup_path, FILE)
|
|
except Exception as e2:
|
|
LOG.error(f"Error: Failed to restore backup: {e2}")
|
|
return
|
|
|
|
# Remove backup on success
|
|
os.remove(backup_path)
|
|
|
|
|
|
def restart_vim_services():
|
|
services = ["vim", "vim-api", "vim-webserver"]
|
|
for service in services:
|
|
command = ["sudo", "sm-restart-safe", "service", service]
|
|
try:
|
|
result = subprocess.run(command,
|
|
check=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True)
|
|
print(f"Successfully restarted {service}:\n{result.stdout}")
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"Error restarting {service}:\n{e.stderr}")
|
|
|
|
|
|
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
|
|
# 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)
|
|
)
|
|
|
|
try:
|
|
if os.path.isfile(RESTART_SERVICES_FLAG):
|
|
restart_vim_services()
|
|
apply_fix_to_set_activate_done_state()
|
|
|
|
open(ACTIVATE_DONE_FLAG, 'a').close()
|
|
os.remove(RESTART_SERVICES_FLAG)
|
|
|
|
# Restart software-controller service
|
|
subprocess.run(["pmon-restart", "software-controller-daemon"], check=True)
|
|
except Exception as e:
|
|
LOG.error(f"Activate script to restart services failed: {e}")
|
|
try:
|
|
os.remove(ACTIVATE_DONE_FLAG)
|
|
open(RESTART_SERVICES_FLAG, 'a').close()
|
|
update_deploy_state("deploy-activate", deploy_state=DEPLOY_STATES.ACTIVATE_FAILED.value)
|
|
except Exception as e:
|
|
LOG.error(f"Activate script to restart services failed twice: {e}")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|