diff --git a/.github/workflows/build-rpm.yml b/.github/workflows/build-rpm.yml index d2d5e8f..e705ebe 100644 --- a/.github/workflows/build-rpm.yml +++ b/.github/workflows/build-rpm.yml @@ -163,7 +163,6 @@ jobs: python3 /opt/linupdate/linupdate.py --profile container python3 /opt/linupdate/linupdate.py --env test - # Try to install package on latest Fedora install-fedora: name: Install on latest Fedora diff --git a/linupdate-agent.py b/linupdate-agent.py index 005941c..5de5ed2 100644 --- a/linupdate-agent.py +++ b/linupdate-agent.py @@ -3,18 +3,24 @@ # Import libraries import time +import sys # Import classes from src.controllers.App.Service import Service # Leave some time for the system to boot # TODO -# time.sleep(60) +# time.sleep(30) # Instantiate Service class my_service = Service() -# Execute main function +# If an argument is passed, execute the corresponding module agent +if len(sys.argv) > 1: + my_service.run_agent(sys.argv[1]) + exit(0) + +# Else, execute main function my_service.main() exit(0) diff --git a/service/linupdate.systemd.template b/service/linupdate.systemd.template index 7d099fa..8ecb29a 100644 --- a/service/linupdate.systemd.template +++ b/service/linupdate.systemd.template @@ -3,7 +3,7 @@ Description=linupdate-agent [Service] Type=simple -ExecStart=/opt/linupdate/service/linupdate-agent.py +ExecStart=/opt/linupdate/linupdate-agent.py [Install] WantedBy=multi-user.target \ No newline at end of file diff --git a/src/controllers/App/Service.py b/src/controllers/App/Service.py index 936037d..819214c 100644 --- a/src/controllers/App/Service.py +++ b/src/controllers/App/Service.py @@ -6,6 +6,7 @@ import sys import importlib import subprocess +import time from pathlib import Path # Import classes @@ -13,73 +14,89 @@ class Service: def __init__(self): + # Register signal handlers + signal.signal(signal.SIGTERM, self.signal_handler) + signal.signal(signal.SIGINT, self.signal_handler) + self.child_processes = [] self.moduleController = Module() + #------------------------------------------------------------------------------------------------------------------- # - # Check if a restart of this service is needed, and restart it if needed + # Service main function # #------------------------------------------------------------------------------------------------------------------- - def restart_self_if_needed(self): - if Path('/tmp/linupdate-service.restart').is_file(): - # Only restart the service if linupdate is not running otherwise it could cut off a running update... - if Path('/tmp/linupdate.lock').is_file(): - return - - print('A restart of this service is required. Restarting...') - Path('/tmp/linupdate-service.restart').unlink() - subprocess.run(["systemctl", "restart", "linupdate.service"]) - - result = subprocess.run( - ["systemctl", "--quiet", "restart", "linupdate.service"], - capture_output = True, - text = True - ) - - # If service could not be restarted, print error and exit - if result.returncode != 0: - print('Error: could not restart linupdate service: ' + result.stderr) - exit(1) + def main(self): + try: + while True: + # Retrieve enabled modules + enabled_modules = self.moduleController.getEnabled() + + # For each enabled module, check if its agent is enabled + for module in enabled_modules: + # Convert module name to uppercase first letter + module_capitalize = module.capitalize() + + # Import python module config class + module_import_path = importlib.import_module('src.controllers.Module.' + module_capitalize + '.Config') + module_class = getattr(module_import_path, 'Config') + + # Instantiate module and get module configuration + my_module = module_class() + module_configuration = my_module.getConf() + + # Check if module agent is enabled. If not, skip to next module + if not module_configuration['agent']['enabled']: + continue + + # Check if module agent process is already running, if so, skip to next module + if any(agent['agent'] == module for agent in self.child_processes): + print('Module ' + module + ' agent is already running') + continue + + # Start module agent + print('Starting ' + module + ' module agent') + + # Start module agent as a child process + process = subprocess.Popen( + ['/opt/linupdate/linupdate-agent.py', module], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + + # Add child process to list + self.child_processes.append({ + 'agent': module, + 'process': process + }) + + print("I'm linupdate service") + time.sleep(5) + except Exception as e: + print('Linupdate service error:' + str(e)) + exit(1) + #------------------------------------------------------------------------------------------------------------------- # - # Service main function + # Run a module agent as a child process # #------------------------------------------------------------------------------------------------------------------- - def main(self): + def run_agent(self, module_name): try: - # Check if a restart of this service is needed - self.restart_self_if_needed() - - # Retrieve enabled modules - enabled_modules = self.moduleController.getEnabled() - - # For each enabled module, check if its agent is enabled - for module in enabled_modules: - # Convert module name to uppercase first letter - module_name = module.capitalize() + # Convert module name to uppercase first letter + module_name_capitalize = module_name.capitalize() - # Import python module config class - module_import_path = importlib.import_module('src.controllers.Module.' + module_name + '.Config') - module_class = getattr(module_import_path, 'Config') + while True: + print("I'm reposerver module agent") + time.sleep(5) - # Instantiate module and get module configuration - my_module = module_class() - module_configuration = my_module.getConf() - - # Check if agent is enabled - if module_configuration['agent']['enabled']: - print('Executing agent for module ' + module_name) - - # Import python module agent class - module_import_path = importlib.import_module('src.controllers.Module.' + module_name + '.Agent') - my_module_agent_class = getattr(module_import_path, 'Agent')() - - # Instantiate module and call module agent main method in a child process - # process = subprocess.Popen(['python3', '-c', 'import src.controllers.Module.' + module_name + '.Agent; src.controllers.Module.' + module_name + '.Agent.main()']) - # self.child_processes.append(process) + # Import python module agent class + # module_import_path = importlib.import_module('src.controllers.Module.' + module_name + '.Agent') + # my_module_agent_class = getattr(module_import_path, 'Agent')() + except Exception as e: print('Linupdate service error:' + str(e)) @@ -92,33 +109,34 @@ def main(self): # #------------------------------------------------------------------------------------------------------------------- def stop_child_processes(self): - for process in self.child_processes: + if not self.child_processes: + print('No child processes') + return + + # Stop all child processes + for child in self.child_processes: + # Retrieve agent name and process + agent = child['agent'] + process = child['process'] + + print('Stopping ' + agent + ' module agent') + + # Terminate process process.terminate() + try: process.wait(timeout=5) except subprocess.TimeoutExpired: process.kill() -#------------------------------------------------------------------------------------------------------------------- -# -# Signal handler -# This function is called when the service receives a SIGTERM or SIGINT signal -# -#------------------------------------------------------------------------------------------------------------------- -def signal_handler(sig, frame): - print('Linupdate service received signal ' + str(sig) + '. Stopping all child processes') - service.stop_child_processes() - sys.exit(0) - - -#------------------------------------------------------------------------------------------------------------------- -# -# Main -# -#------------------------------------------------------------------------------------------------------------------- -if __name__ == "__main__": - service = Service() - signal.signal(signal.SIGTERM, signal_handler) - signal.signal(signal.SIGINT, signal_handler) - service.main() + #------------------------------------------------------------------------------------------------------------------- + # + # Signal handler + # This function is called when the service receives a SIGTERM or SIGINT signal + # + #------------------------------------------------------------------------------------------------------------------- + def signal_handler(self, sig, frame): + print('Linupdate service received signal ' + str(sig) + '. Stopping all child processes...') + self.stop_child_processes() + sys.exit(0) diff --git a/src/controllers/Args.py b/src/controllers/Args.py index acddd01..590877d 100644 --- a/src/controllers/Args.py +++ b/src/controllers/Args.py @@ -396,6 +396,8 @@ def parse(self): myExit.cleanExit() except Exception as e: raise Exception('Could not enable module: ' + str(e)) + else: + raise Exception('Module name is required') # # If --mod-disable param has been set @@ -409,20 +411,8 @@ def parse(self): myExit.cleanExit() except Exception as e: raise Exception('Could not disable module: ' + str(e)) - - # - # If --mod-configure param has been set - # - # if args.mod_configure != "null": - # print(args.mod_configure) - # # If module to configure is set (not 'None'), configure the module - # if args.mod_configure: - # try: - # # Configure module - # myModule.configure(args.mod_configure) - # myExit.cleanExit() - # except Exception as e: - # raise Exception('Could not configure ' + args.mod_configure + ' module: ' + str(e)) + else: + raise Exception('Module name is required') except Exception as e: raise Exception(str(e))