diff --git a/.gitignore b/.gitignore index e374a01..ff433d5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ __pycache__/ # C extensions *.so - +config/*.yaml # Distribution / packaging .Python build/ diff --git a/cli.py b/cli.py index 13bcbdf..b2ed4a7 100644 --- a/cli.py +++ b/cli.py @@ -3,6 +3,7 @@ import argparse import tqdm from services.odoo.service import OdooServiceManager from services.odoo.module import OdooModuleManager +import lib.color_log as color_log def service(args): @@ -15,7 +16,10 @@ def service(args): case "restart": service.restart_service(args.instance) case _: - print("Invalid action") + color_log.Show( + "FAILED", + f"Invalid action '{args.action}' for service management.", + ) def module(args): @@ -23,12 +27,17 @@ def module(args): # If modules are provided in the command line if args.modules: - print(f"Processing modules: {', '.join(args.modules)} for {args.instance}") + color_log.Show( + "INFO", + f"Processing modules: {', '.join(args.modules)} for {args.instance}", + ) else: # Fallback if no modules are provided (can use default from instance settings) - print(f"No modules specified. Using default modules for {args.instance}") + color_log.Show( + "INFO", + f"No modules specified. Using default modules for {args.instance}", + ) args.modules = module_manager.get_modules(args.instance) - # Create a progress bar using tqdm for module_name in tqdm.tqdm( args.modules, desc="Processing modules", unit="module" @@ -41,7 +50,10 @@ def module(args): case "upgrade": module_manager.upgrade(args.instance, [module_name]) case _: - print("Invalid action") + color_log.Show( + "FAILED", + f"Invalid action '{args.action}' for module management.", + ) def setup_cli(): diff --git a/config/settings.template b/config/settings.template new file mode 100644 index 0000000..4ba30d3 --- /dev/null +++ b/config/settings.template @@ -0,0 +1,54 @@ +odoo_instances: +- name: "ftacpa" + host: "10.1.1.31" + port: 8069 + database: "ftacpa" + username: "nextzen" + password: "smartyourlife" + type: "systemctl" + service_name: "odoo18" + git: + repo_url: "https://hoangvv:smartyourlife@git.nextzenos.com/NextERP/Odoo18-FTACPA.git" + branch: "community/demo/ftacpa" + local_path: "/opt/odoo18/addons" + ssh: + user: root + password: Smartyourlife123@* + key_path: "/root/.ssh/privatessh.key" +odoo_instances: +- name: "ftacpa" + host: "10.1.1.31" + port: 8069 + database: "server" + username: "nextzen" + password: "smartyourlife" + type: "systemctl" + service_name: "odoo18" + git: + repo_url: "https://hoangvv:smartyourlife@git.nextzenos.com/NextERP/Odoo18-FTACPA.git" + branch: "community/demo/ftacpa" + local_path: "/opt/ftacpa/addons" + ssh: + user: root + password: Smartyourlife123@* + key_path: "/root/.ssh/privatessh.key" + # - name: "server1_test" + # host: "server1.example.com" + # port: 8069 + # database: "test_db1" + # username: "admin" + # password: "test_password" + # module_names: + # - "your_module" + # type: "docker" + # - name: "server2_prod" + # host: "server2.example.com" + # port: 8070 + # database: "prod_db2" + # username: "admin" + # password: "admin_password" + # module_names: + # - "your_module" + # - "custom_module" + # - "third_module" + # type: "systemctl" diff --git a/lib/color_log.py b/lib/color_log.py index 58d51a1..91241e1 100644 --- a/lib/color_log.py +++ b/lib/color_log.py @@ -9,16 +9,16 @@ def colorize(text, code): def Show(status, message): """Displays a message with a status indicator.""" colors = { - 0: ( + 'OK': ( colorize("[", "90") + colorize(" OK ", "38;5;154") + colorize("]", "90") ), # Green, Grey - 1: ( + 'FAILED': ( colorize("[", "90") + colorize(" FAILED ", "91") + colorize("]", "90") ), # Red, Grey - 2: ( + 'INFO': ( colorize("[", "90") + colorize(" INFO ", "38;5;154") + colorize("]", "90") ), # Green, Grey - 3: ( + 'WARNING': ( colorize("[", "90") + colorize(" WARNING ", "33") + colorize("]", "90") ), # Yellow, Grey } diff --git a/services/odoo/connection.py b/services/odoo/connection.py index 9f04d59..1fe6842 100644 --- a/services/odoo/connection.py +++ b/services/odoo/connection.py @@ -2,6 +2,7 @@ from odoorpc import ODOO import os import sys from services.config import Config +import lib.color_log as color_log class OdooConnection: @@ -48,11 +49,15 @@ class OdooConnection: instance["database"], instance["username"], instance["password"] ) self.connections[instance["name"]] = odoo - print( - f"Connected to {instance['host']}:{instance['port']} - {instance['database']}" + color_log.Show( + "OK", + f"Connected to {instance['name']} at {instance['host']}:{instance['port']}", ) except Exception as e: - print(f"Failed to connect to {instance['name']}: {e}") + color_log.Show( + "FAILED", + f"Failed to connect to {instance['name']} at {instance['host']}:{instance['port']}: {e}", + ) raise def get_connection(self, instance_name): @@ -69,7 +74,7 @@ class OdooConnection: if name in self.connections: # odoorpc doesn't have an explicit disconnect, so we just remove the reference del self.connections[name] - print(f"Disconnected from instance: {name}") + color_log.Show("OK", f"Disconnected from {name}") def get_instances(self): """Return the list of configured instances.""" @@ -80,11 +85,13 @@ class OdooConnection: odoo = self.get_connection(instance_name) if not odoo: raise ValueError(f"No connection available for instance '{instance_name}'") - try: model_obj = odoo.env[model] result = getattr(model_obj, method)(*args, **kwargs) return result except Exception as e: - print(f"Error executing {method} on {model} for {instance_name}: {e}") + color_log.Show( + "FAILED", + f"Error executing {method} on {model} for {instance_name}: {e}", + ) raise diff --git a/services/odoo/module.py b/services/odoo/module.py index f581d9b..305f650 100644 --- a/services/odoo/module.py +++ b/services/odoo/module.py @@ -1,6 +1,7 @@ from services.git.handler import GitHandler from services.odoo.connection import OdooConnection import subprocess +import lib.color_log as color_log class OdooModuleManager: @@ -19,14 +20,13 @@ class OdooModuleManager: for instance in self.config.get_instances(): if instance_name and instance["name"] != instance_name: continue - - print(f"Processing instance: {instance['name']}") + color_log.Show("INFO", f"Processing instance: {instance['name']}") for module_name in module_names: try: - print( - f"{action.capitalize()}ing module: {module_name} in {instance['name']}" + color_log.Show( + "INFO", + f"{action.capitalize()}ing module: {module_name} in {instance['name']}", ) - module_ids = self.config.execute( instance["name"], "ir.module.module", @@ -34,9 +34,10 @@ class OdooModuleManager: [("name", "=", module_name)], ) - if not module_ids: - print( - f"Module {module_name} not found in {instance['name']}, skipping." + if not module_ids and action in ["upgrade", "uninstall"]: + color_log.Show( + "WARNING", + f"Module {module_name} not found in {instance['name']}, skipping.", ) continue @@ -47,23 +48,41 @@ class OdooModuleManager: button_action, module_ids, # Pass list directly instead of wrapping in another list ) - - print( - f"Module {module_name} {action}ed successfully in {instance['name']}" + color_log.Show( + "OK", + f"Module {module_name} {action}ed successfully in {instance['name']}", ) except Exception as e: - print( - f"Error while {action}ing {module_name} in {instance['name']}: {e}" + color_log.Show( + "FAILED", + f"Error while {action}ing {module_name} in {instance['name']}: {e}", ) - def install(self, instance_name: str = None,module_names: list = None) -> None: + def get_modules(self, instance_name: str = None) -> list: + """Get a list of installed modules for the specified instance(s).""" + self.config.connect(instance_name) + modules = [] + for instance in self.config.get_instances(): + if instance_name and instance["name"] != instance_name: + continue + color_log.Show("INFO", f"Fetching modules for instance: {instance['name']}") + module_ids = self.config.execute( + instance["name"], + "ir.module.module", + "search_read", + [], + ["name"], + ) + modules.extend([mod["name"] for mod in module_ids]) + return modules + def install(self, instance_name: str = None, module_names: list = None) -> None: """Install multiple modules for the specified instance(s).""" - self._manage_module("install", instance_name,module_names) + self._manage_module("install", instance_name, module_names) - def uninstall(self, instance_name: str = None,module_names: list = None) -> None: + def uninstall(self, instance_name: str = None, module_names: list = None) -> None: """Uninstall multiple modules for the specified instance(s).""" - self._manage_module("uninstall", instance_name,module_names) + self._manage_module("uninstall", instance_name, module_names) - def upgrade(self, instance_name: str = None,module_names: list = None) -> None: + def upgrade(self, instance_name: str = None, module_names: list = None) -> None: """Upgrade multiple modules for the specified instance(s).""" - self._manage_module("upgrade", instance_name,module_names) + self._manage_module("upgrade", instance_name, module_names) diff --git a/services/odoo/service.py b/services/odoo/service.py index 3c76358..53872fc 100644 --- a/services/odoo/service.py +++ b/services/odoo/service.py @@ -1,5 +1,6 @@ import subprocess from services.odoo.connection import OdooConnection +import lib.color_log as color_log class OdooServiceManager: @@ -9,11 +10,15 @@ class OdooServiceManager: def _execute_command(self, cmd, instance_name): """Execute a shell command and handle errors.""" try: - print(f"Executing: {cmd}") + color_log.Show("INFO", f"Executing command: {cmd}") subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True) - print(f"Service operation successful for {instance_name}") + color_log.Show("OK", f"Command executed successfully for {instance_name}") except subprocess.CalledProcessError as e: - print(f"Error performing service operation for {instance_name}: {e}") + color_log.Show( + "FAILED", + f"Error performing service operation for {instance_name}: {e}", + ) + raise def _get_command(self, instance, action): @@ -35,7 +40,10 @@ class OdooServiceManager: container_name = instance.get("container_name", f"odoo_{instance['name']}") cmd = f"docker {action} {container_name}" else: - print(f"Unsupported service type '{service_type}' for {instance['name']}") + color_log.Show( + "WARNING", + f"Unsupported service type '{service_type}' for {instance['name']}", + ) return None if not local_host: @@ -52,14 +60,16 @@ class OdooServiceManager: :param action: "stop" or "restart" :param instance_name: Specific instance name, or None for all instances. """ - if action not in ["stop", "restart","start"]: + if action not in ["stop", "restart", "start"]: raise ValueError("Action must be 'stop' or 'restart'.") for instance in self.config.get_instances(): if instance_name and instance["name"] != instance_name: continue - - print(f"{action.capitalize()}ing service for instance: {instance['name']}") + color_log.Show( + "INFO", + f"{action.capitalize()}ing service for instance: {instance['name']}", + ) cmd = self._get_command(instance, action) if cmd: self._execute_command(cmd, instance["name"])