diff --git a/opt/torero-mcp/torero_mcp/executor.py b/opt/torero-mcp/torero_mcp/executor.py index 3539313..9b07b9b 100644 --- a/opt/torero-mcp/torero_mcp/executor.py +++ b/opt/torero-mcp/torero_mcp/executor.py @@ -244,46 +244,181 @@ async def get_registries(self) -> List[Dict[str, Any]]: raise ToreroExecutorError(f"unexpected json structure: {type(raw_output)}") # service execution operations - async def run_ansible_playbook_service(self, name: str, **kwargs) -> Dict[str, Any]: - """execute ansible playbook service.""" - command = ["run", "service", "ansible-playbook", name, "--raw"] + async def run_ansible_playbook_service( + self, + name: str, + set_vars: Optional[Dict[str, str]] = None, + set_secrets: Optional[List[str]] = None, + use_decorator: bool = False, + **kwargs + ) -> Dict[str, Any]: + """execute ansible playbook service with full parameter support. + + args: + name: service name + set_vars: dict of key=value pairs for --set parameters + set_secrets: list of secret names for --set-secret parameters + use_decorator: whether to display possible inputs via decorator (--use flag) + **kwargs: additional parameters for backward compatibility + """ + command = ["run", "service", "ansible-playbook", name] + + # add --set parameters + if set_vars: + for key, value in set_vars.items(): + command.extend(["--set", f"{key}={value}"]) - # add additional parameters - for key, value in kwargs.items(): - if value is not None: - command.append(f"--{key}={value}") + # add --set-secret parameters + if set_secrets: + for secret in set_secrets: + command.extend(["--set-secret", secret]) + + # add --use flag + if use_decorator: + command.append("--use") + + # add --raw for consistent output format + command.append("--raw") return await self.execute_command(command, timeout=300) # 5 min timeout - async def run_python_script_service(self, name: str, **kwargs) -> Dict[str, Any]: - """execute python script service.""" - command = ["run", "service", "python-script", name, "--raw"] + async def run_python_script_service( + self, + name: str, + set_vars: Optional[Dict[str, str]] = None, + set_secrets: Optional[List[str]] = None, + use_decorator: bool = False, + **kwargs + ) -> Dict[str, Any]: + """execute python script service with full parameter support. - for key, value in kwargs.items(): - if value is not None: - command.append(f"--{key}={value}") + args: + name: service name + set_vars: dict of key=value pairs for --set parameters + set_secrets: list of secret names for --set-secret parameters + use_decorator: whether to display possible inputs via decorator (--use flag) + **kwargs: additional parameters for backward compatibility + """ + command = ["run", "service", "python-script", name] + + # add --set parameters + if set_vars: + for key, value in set_vars.items(): + command.extend(["--set", f"{key}={value}"]) + + # add --set-secret parameters + if set_secrets: + for secret in set_secrets: + command.extend(["--set-secret", secret]) + + # add --use flag + if use_decorator: + command.append("--use") + + # add --raw for consistent output format + command.append("--raw") return await self.execute_command(command, timeout=300) - async def run_opentofu_plan_apply_service(self, name: str, **kwargs) -> Dict[str, Any]: - """execute opentofu plan apply service.""" - command = ["run", "service", "opentofu-plan", "apply", name, "--raw"] + async def run_opentofu_plan_apply_service( + self, + name: str, + set_vars: Optional[Dict[str, str]] = None, + set_secrets: Optional[List[str]] = None, + state: Optional[str] = None, + state_out: Optional[str] = None, + use_decorator: bool = False, + **kwargs + ) -> Dict[str, Any]: + """execute opentofu plan apply service with full parameter support. - for key, value in kwargs.items(): - if value is not None: - command.append(f"--{key}={value}") + args: + name: service name + set_vars: dict of key=value pairs for --set parameters + set_secrets: list of secret names for --set-secret parameters + state: state file to utilize (JSON or @file path) + state_out: path to write resulting state file + use_decorator: whether to display possible inputs via decorator (--use flag) + **kwargs: additional parameters for backward compatibility + """ + command = ["run", "service", "opentofu-plan", "apply", name] + + # add --set parameters + if set_vars: + for key, value in set_vars.items(): + command.extend(["--set", f"{key}={value}"]) + + # add --set-secret parameters + if set_secrets: + for secret in set_secrets: + command.extend(["--set-secret", secret]) + + # add --state parameter + if state: + command.extend(["--state", state]) + + # add --state-out parameter + if state_out: + command.extend(["--state-out", state_out]) + + # add --use flag + if use_decorator: + command.append("--use") + + # add --raw for consistent output format + command.append("--raw") return await self.execute_command(command, timeout=600) # 10 min timeout - async def run_opentofu_plan_destroy_service(self, name: str, **kwargs) -> Dict[str, Any]: - """execute opentofu plan destroy service.""" - command = ["run", "service", "opentofu-plan", "destroy", name, "--raw"] + async def run_opentofu_plan_destroy_service( + self, + name: str, + set_vars: Optional[Dict[str, str]] = None, + set_secrets: Optional[List[str]] = None, + state: Optional[str] = None, + state_out: Optional[str] = None, + use_decorator: bool = False, + **kwargs + ) -> Dict[str, Any]: + """execute opentofu plan destroy service with full parameter support. - for key, value in kwargs.items(): - if value is not None: - command.append(f"--{key}={value}") + args: + name: service name + set_vars: dict of key=value pairs for --set parameters + set_secrets: list of secret names for --set-secret parameters + state: state file to utilize (JSON or @file path) + state_out: path to write resulting state file + use_decorator: whether to display possible inputs via decorator (--use flag) + **kwargs: additional parameters for backward compatibility + """ + command = ["run", "service", "opentofu-plan", "destroy", name] + + # add --set parameters + if set_vars: + for key, value in set_vars.items(): + command.extend(["--set", f"{key}={value}"]) + + # add --set-secret parameters + if set_secrets: + for secret in set_secrets: + command.extend(["--set-secret", secret]) + + # add --state parameter + if state: + command.extend(["--state", state]) - return await self.execute_command(command, timeout=600) + # add --state-out parameter + if state_out: + command.extend(["--state-out", state_out]) + + # add --use flag + if use_decorator: + command.append("--use") + + # add --raw for consistent output format + command.append("--raw") + + return await self.execute_command(command, timeout=600) # 10 min timeout # database operations async def export_database(self, format: str = "yaml") -> Dict[str, Any]: diff --git a/opt/torero-mcp/torero_mcp/tools/execution_tools.py b/opt/torero-mcp/torero_mcp/tools/execution_tools.py index eea7ef4..cab5f4b 100644 --- a/opt/torero-mcp/torero_mcp/tools/execution_tools.py +++ b/opt/torero-mcp/torero_mcp/tools/execution_tools.py @@ -9,19 +9,48 @@ logger = logging.getLogger(__name__) -async def execute_ansible_playbook(executor: ToreroExecutor, service_name: str) -> str: +async def execute_ansible_playbook( + executor: ToreroExecutor, + service_name: str, + set_vars: Optional[str] = None, + set_secrets: Optional[str] = None, + use_decorator: bool = False +) -> str: """ - execute an ansible-playbook service. + Execute an Ansible playbook service with comprehensive parameter support. - args: - executor: toreroexecutor instance - service_name: name of the ansible-playbook service to execute + Args: + executor: ToreroExecutor instance + service_name: Name of the ansible-playbook service to execute + set_vars: JSON string of key=value pairs for --set parameters (e.g., '{"interface": "0/0/0", "environment": "production"}') + set_secrets: Comma-separated list of secret names for --set-secret parameters (e.g., "db_password,api_key") + use_decorator: Whether to display possible inputs via decorator (--use flag) - returns: - json string containing execution result with return_code, stdout, stderr, and timing information + Returns: + JSON string containing execution result with return_code, stdout, stderr, and timing information """ try: - result = await executor.run_ansible_playbook_service(service_name) + # parse set_vars if provided + parsed_set_vars = None + if set_vars: + try: + parsed_set_vars = json.loads(set_vars) + if not isinstance(parsed_set_vars, dict): + return f"error: set_vars must be a JSON object with key-value pairs" + except json.JSONDecodeError as e: + return f"error: invalid JSON in set_vars: {e}" + + # parse set_secrets if provided + parsed_set_secrets = None + if set_secrets: + parsed_set_secrets = [secret.strip() for secret in set_secrets.split(",") if secret.strip()] + + result = await executor.run_ansible_playbook_service( + service_name, + set_vars=parsed_set_vars, + set_secrets=parsed_set_secrets, + use_decorator=use_decorator + ) return json.dumps(result, indent=2) except ToreroExecutorError as e: return f"error executing ansible-playbook service '{service_name}': {e}" @@ -30,19 +59,48 @@ async def execute_ansible_playbook(executor: ToreroExecutor, service_name: str) return f"unexpected error: {e}" -async def execute_python_script(executor: ToreroExecutor, service_name: str) -> str: +async def execute_python_script( + executor: ToreroExecutor, + service_name: str, + set_vars: Optional[str] = None, + set_secrets: Optional[str] = None, + use_decorator: bool = False +) -> str: """ - execute a python-script service. + Execute a Python script service with comprehensive parameter support. - args: - executor: toreroexecutor instance - service_name: name of the python-script service to execute + Args: + executor: ToreroExecutor instance + service_name: Name of the python-script service to execute + set_vars: JSON string of key=value pairs for --set parameters (e.g., '{"device": "10.0.0.1", "commands": "[\"show ver\"]"}') + set_secrets: Comma-separated list of secret names for --set-secret parameters (e.g., "db_password,api_key") + use_decorator: Whether to display possible inputs via decorator (--use flag) - returns: - json string containing execution result with return_code, stdout, stderr, and timing information + Returns: + JSON string containing execution result with return_code, stdout, stderr, and timing information """ try: - result = await executor.run_python_script_service(service_name) + # parse set_vars if provided + parsed_set_vars = None + if set_vars: + try: + parsed_set_vars = json.loads(set_vars) + if not isinstance(parsed_set_vars, dict): + return f"error: set_vars must be a JSON object with key-value pairs" + except json.JSONDecodeError as e: + return f"error: invalid JSON in set_vars: {e}" + + # parse set_secrets if provided + parsed_set_secrets = None + if set_secrets: + parsed_set_secrets = [secret.strip() for secret in set_secrets.split(",") if secret.strip()] + + result = await executor.run_python_script_service( + service_name, + set_vars=parsed_set_vars, + set_secrets=parsed_set_secrets, + use_decorator=use_decorator + ) return json.dumps(result, indent=2) except ToreroExecutorError as e: return f"error executing python-script service '{service_name}': {e}" @@ -51,19 +109,54 @@ async def execute_python_script(executor: ToreroExecutor, service_name: str) -> return f"unexpected error: {e}" -async def execute_opentofu_plan_apply(executor: ToreroExecutor, service_name: str) -> str: +async def execute_opentofu_plan_apply( + executor: ToreroExecutor, + service_name: str, + set_vars: Optional[str] = None, + set_secrets: Optional[str] = None, + state: Optional[str] = None, + state_out: Optional[str] = None, + use_decorator: bool = False +) -> str: """ - execute an opentofu plan service to apply infrastructure changes. + Execute an OpenTofu plan service to apply infrastructure changes with comprehensive parameter support. - args: - executor: toreroexecutor instance - service_name: name of the opentofu plan service to apply + Args: + executor: ToreroExecutor instance + service_name: Name of the opentofu plan service to apply + set_vars: JSON string of key=value pairs for --set parameters (e.g., '{"server_name": "web01", "region": "us-west-2"}') + set_secrets: Comma-separated list of secret names for --set-secret parameters (e.g., "aws_secret,db_password") + state: State file to utilize - JSON string or file path with @ prefix (e.g., "@opentofu.tfstate") + state_out: Path to write the resulting state file (e.g., "@updated_state.tfstate") + use_decorator: Whether to display possible inputs via decorator (--use flag) - returns: - json string containing execution result with return_code, stdout, stderr, and timing information + Returns: + JSON string containing execution result with return_code, stdout, stderr, and timing information """ try: - result = await executor.run_opentofu_plan_apply_service(service_name) + # parse set_vars if provided + parsed_set_vars = None + if set_vars: + try: + parsed_set_vars = json.loads(set_vars) + if not isinstance(parsed_set_vars, dict): + return f"error: set_vars must be a JSON object with key-value pairs" + except json.JSONDecodeError as e: + return f"error: invalid JSON in set_vars: {e}" + + # parse set_secrets if provided + parsed_set_secrets = None + if set_secrets: + parsed_set_secrets = [secret.strip() for secret in set_secrets.split(",") if secret.strip()] + + result = await executor.run_opentofu_plan_apply_service( + service_name, + set_vars=parsed_set_vars, + set_secrets=parsed_set_secrets, + state=state, + state_out=state_out, + use_decorator=use_decorator + ) return json.dumps(result, indent=2) except ToreroExecutorError as e: return f"error executing opentofu plan apply service '{service_name}': {e}" @@ -72,19 +165,54 @@ async def execute_opentofu_plan_apply(executor: ToreroExecutor, service_name: st return f"unexpected error: {e}" -async def execute_opentofu_plan_destroy(executor: ToreroExecutor, service_name: str) -> str: +async def execute_opentofu_plan_destroy( + executor: ToreroExecutor, + service_name: str, + set_vars: Optional[str] = None, + set_secrets: Optional[str] = None, + state: Optional[str] = None, + state_out: Optional[str] = None, + use_decorator: bool = False +) -> str: """ - execute an opentofu plan service to destroy infrastructure resources. + Execute an OpenTofu plan service to destroy infrastructure resources with comprehensive parameter support. - args: - executor: toreroexecutor instance - service_name: name of the opentofu plan service to destroy + Args: + executor: ToreroExecutor instance + service_name: Name of the opentofu plan service to destroy + set_vars: JSON string of key=value pairs for --set parameters (e.g., '{"server_name": "web01", "region": "us-west-2"}') + set_secrets: Comma-separated list of secret names for --set-secret parameters (e.g., "aws_secret,db_password") + state: State file to utilize - JSON string or file path with @ prefix (e.g., "@opentofu.tfstate") + state_out: Path to write the resulting state file (e.g., "@updated_state.tfstate") + use_decorator: Whether to display possible inputs via decorator (--use flag) - returns: - json string containing execution result with return_code, stdout, stderr, and timing information + Returns: + JSON string containing execution result with return_code, stdout, stderr, and timing information """ try: - result = await executor.run_opentofu_plan_destroy_service(service_name) + # parse set_vars if provided + parsed_set_vars = None + if set_vars: + try: + parsed_set_vars = json.loads(set_vars) + if not isinstance(parsed_set_vars, dict): + return f"error: set_vars must be a JSON object with key-value pairs" + except json.JSONDecodeError as e: + return f"error: invalid JSON in set_vars: {e}" + + # parse set_secrets if provided + parsed_set_secrets = None + if set_secrets: + parsed_set_secrets = [secret.strip() for secret in set_secrets.split(",") if secret.strip()] + + result = await executor.run_opentofu_plan_destroy_service( + service_name, + set_vars=parsed_set_vars, + set_secrets=parsed_set_secrets, + state=state, + state_out=state_out, + use_decorator=use_decorator + ) return json.dumps(result, indent=2) except ToreroExecutorError as e: return f"error executing opentofu plan destroy service '{service_name}': {e}"