diff --git a/addons/cetmix_tower_server/README.rst b/addons/cetmix_tower_server/README.rst deleted file mode 100644 index 7718d80..0000000 --- a/addons/cetmix_tower_server/README.rst +++ /dev/null @@ -1,318 +0,0 @@ -=================== -Cetmix Tower Server -=================== - -.. - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! This file is generated by oca-gen-addon-readme !! - !! changes will be overwritten. !! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:426fb7e35a73ec875cf2b484e24211df3f52fc22d9d637f4f3a86bc23ac2e05f - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png - :target: https://odoo-community.org/page/development-status - :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 -.. |badge3| image:: https://img.shields.io/badge/github-cetmix%2Fcetmix--tower-lightgray.png?logo=github - :target: https://github.com/cetmix/cetmix-tower/tree/16.0/cetmix_tower_server - :alt: cetmix/cetmix-tower - -|badge1| |badge2| |badge3| - -`Cetmix Tower `__ offers a streamlined -solution for managing remote servers and applications via SSH or API -calls directly from `Odoo `__. It is designed for -versatility across different operating systems and software -environments, providing a practical option for those looking to manage -servers without getting tied down by vendor or technology constraints. - -Please refer to the `official -documentation `__ for detailed information. - -**Table of contents** - -.. contents:: - :local: - -Configuration -============= - -Please refer to the `official -documentation `__ for detailed configuration -instructions. - -Usage -===== - -Please refer to the `official -documentation `__ for detailed usage -instructions. - -Changelog -========= - -16.0.2.2.8 (2025-12-22) ------------------------ - -- Bugfixes: Handle malformed expressions in flight plan line conditions. - (5154) - -16.0.2.2.7 (2025-12-16) ------------------------ - -- Features: Support for ANSI formatting in server logs. (5141) - -- Bugfixes: UI/UX fixed and improvements. (5141) - -16.0.2.2.6 (2025-12-11) ------------------------ - -- Features: Improve search views, implement the search panel for - selected views. (5139) - -16.0.2.2.5 (2025-12-10) ------------------------ - -- Bugfixes: Custom values in flight plan are lost in a skipped command - and are not available after it. (5129) - -16.0.2.2.4 (2025-12-10) ------------------------ - -- Features: Parse empty or missing key values as 'None' instead of - leaving key reference as is. (5134) - -16.0.2.2.3 (2025-12-03) ------------------------ - -- Bugfixes: Save correct error message in log when SSH connection fails. - (5109) - -16.0.2.2.2 (2025-12-03) ------------------------ - -- Bugfixes: Make variables selectable in scheduled tasks (5105) - -16.0.2.2.0 (2025-11-12) ------------------------ - -- Features: Integrate user notifications into the main module, drop the - 'cetmix_tower_notify_backend' module. (5074) - -16.0.2.0.6 (2025-10-27) ------------------------ - -- Features: Tag mixin and helper commands. (5039) - -16.0.2.0.5 (2025-10-16) ------------------------ - -- Bugfixes: Flight plan command exception handling (4930) - -16.0.2.0.4 (2025-10-13) ------------------------ - -- Features: Auto update references for related records (5005) - -16.0.2.0.3 (2025-10-13) ------------------------ - -- Features: Terminate running flight plan manually (3410) - -16.0.2.0.2 (2025-10-08) ------------------------ - -- Features: UI/UX improvements (4996) - -- Bugfixes: Handle secret values when a record is duplicated using - copy() (4996) - -16.0.2.0.1 (2025-10-08) ------------------------ - -- Bugfixes: Improve variable value references uniqueness (4961) - -16.0.2.0.0 (2025-10-07) ------------------------ - -- Features: 'Cetmix Tower Vault' - new way of centralized password/key - management (4824) - -16.0.1.7.2 (2025-09-18) ------------------------ - -- Features: Set 'Auto Sync' in files from file templates (4949) - -16.0.1.7.1 (2025-09-10) ------------------------ - -- Bugfixes: Check custom values in flight plan line condition (4922) - -16.0.1.6.4 (2025-08-18) ------------------------ - -- Features: Improve the extendability of the file upload command. (4759) - -16.0.1.6.3 (2025-08-13) ------------------------ - -- Features: Improve access settings for logs (4866) - -16.0.1.6.2 (2025-08-05) ------------------------ - -- Bugfixes: Pin paramiko version to "<4" to maintain compatibility with - legacy installations (4891) - -16.0.1.6.0 (2025-07-30) ------------------------ - -- Features: Optional behaviour when file uploaded by command already - exists on the server. (4740) - -16.0.1.5.3 (2025-07-29) ------------------------ - -- Features: Make file references server dependent to be more unique - (4870) - -16.0.1.5.1 (2025-07-25) ------------------------ - -- Features: Select secrets from dropdown list in the code fields (4853) - -16.0.1.5.0 (2025-07-22) ------------------------ - -- Features: Select variables from dropdown list in the code fields - (4827) - -16.0.1.3.0 (2025-07-17) ------------------------ - -- Features: Add the tldextract and dnspython libraries. (4737) - -16.0.1.1.4 (2025-07-07) ------------------------ - -- Bugfixes: Command log sorting (4816) - -16.0.1.1.2 (2025-06-25) ------------------------ - -- Features: Required variables in servers (4779) - -16.0.1.1.1 (2025-06-21) ------------------------ - -- Features: Command view improvements (4753) - -16.0.1.1.0 (2025-06-20) ------------------------ - -- Features: Run commands and flight plans using scheduled tasks. (4650) - -16.0.1.0.12 (2025-06-06) ------------------------- - -- Features: Improve command and flight plan log management. (4749) - -16.0.1.0.11 (2025-06-06) ------------------------- - -- Bugfixes: Host key cannot be retrieved from the UI. (4747) - -16.0.1.0.10 (2025-05-24) ------------------------- - -- Features: Improve command log and flight plan form views (4697) - -16.0.1.0.9 (2025-05-23) ------------------------ - -- Bugfixes: Error when rendering a file not attached to a server. (4715) - -16.0.1.0.8 (2025-05-21) ------------------------ - -- Features: References for secret values. (4696) -- Features: Make the "Host key" field non-required in the form view to - improve the UX. (4699) - -16.0.1.0.7 (2025-05-16) ------------------------ - -- Features: Option to preserve command splitting when using sudo​. (4641) - -- Features: Record references for files. (4670) - -- Features: Use ``sudo`` parameter to pass sudo mode to command runner - instead of using context. (4678) - -- Bugfixes: Incorrect sudo usage in commands run in wizard. Pass 'No - split for sudo' property to commands run in wizard. (4679) - -16.0.1.0.6 (2025-05-16) ------------------------ - -- Features: Improve the key storage functionality. (4686) - -16.0.1.0.5 (2025-05-09) ------------------------ - -- Bugfixes: Non-critical issues and performance improvements. (4663) - -16.0.1.0.4 (2025-04-30) ------------------------ - -- Features: UI/UX improvements. (4642) - -16.0.1.0.3 (2025-04-22) ------------------------ - -- Features: Allow to pass custom variable values to commands (4524) - -- Features: Cetmix Tower Odoo Automation model: pass custom variable - values to the ``server_run_command`` method. (4547) - -- Bugfixes: Random id generation, sudo command parsing, record rule - names, spelling errors in descriptions. (4612) - -16.0.1.0.2 (2025-04-22) ------------------------ - -- Bugfixes: Refactor secret value handling, fix the new server template - creation wizard. (4601) - -16.0.1.0.1 ----------- - -Release for Odoo 16.0 - -Bug Tracker -=========== - -Bugs are tracked on `GitHub Issues `_. -In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. - -Do not contact contributors directly about support or help with technical issues. - -Credits -======= - -Authors -------- - -* Cetmix - -Maintainers ------------ - -This module is part of the `cetmix/cetmix-tower `_ project on GitHub. - -You are welcome to contribute. diff --git a/addons/cetmix_tower_server/__init__.py b/addons/cetmix_tower_server/__init__.py deleted file mode 100644 index d8b99ed..0000000 --- a/addons/cetmix_tower_server/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# pylint: disable=E8103 - -from . import models -from . import wizards diff --git a/addons/cetmix_tower_server/__manifest__.py b/addons/cetmix_tower_server/__manifest__.py deleted file mode 100644 index 4b3eda9..0000000 --- a/addons/cetmix_tower_server/__manifest__.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright Cetmix OÜ 2022 -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -{ - "name": "Cetmix Tower Server", - "summary": "Manage servers and applications from Odoo", - "version": "16.0.2.2.9", - "category": "Productivity", - "website": "https://tower.cetmix.com", - "live_test_url": "https://cetmix.com/tower", - "images": ["static/description/banner.png"], - "author": "Cetmix", - "license": "AGPL-3", - "application": False, - "installable": True, - "external_dependencies": { - "python": ["paramiko<4", "tldextract", "dnspython", "ansi2html"], - }, - "depends": [ - "mail", - "rpc_helper", - "web_notify", - ], - "data": [ - "security/cetmix_tower_server_groups.xml", - "security/ir.model.access.csv", - "security/cx_tower_server_security.xml", - "security/cx_tower_command_security.xml", - "security/cx_tower_plan_security.xml", - "security/cx_tower_plan_line_security.xml", - "security/cx_tower_plan_line_action_security.xml", - "security/cx_tower_plan_log_security.xml", - "security/cx_tower_server_log_security.xml", - "security/cx_tower_command_log_security.xml", - "security/cx_tower_server_template_security.xml", - "security/cx_tower_file_security.xml", - "security/cx_tower_file_template_security.xml", - "security/cx_tower_variable_security.xml", - "security/cx_tower_variable_value_security.xml", - "security/cx_tower_variable_option_security.xml", - "security/cx_tower_scheduled_task_security.xml", - "security/cx_tower_scheduled_task_cv_security.xml", - "security/cx_tower_key_security.xml", - "security/cx_tower_key_value_security.xml", - "security/cx_tower_tag_security.xml", - "security/cx_tower_shortcut_security.xml", - "security/cx_tower_server_wizard_access_rules.xml", - "data/ir_actions_server.xml", - "data/ir_cron.xml", - "data/ir_config_parameter.xml", - "wizards/cx_tower_command_run_wizard_view.xml", - "wizards/cx_tower_plan_run_wizard_view.xml", - "wizards/cx_tower_server_template_create_wizard_view.xml", - "wizards/cx_tower_server_host_key_wizard_view.xml", - "views/cx_tower_server_view.xml", - "views/cx_tower_os_view.xml", - "views/cx_tower_tag_view.xml", - "views/cx_tower_variable_view.xml", - "views/cx_tower_variable_value_view.xml", - "views/cx_tower_command_view.xml", - "views/cx_tower_plan_view.xml", - "views/cx_tower_plan_line_view.xml", - "views/cx_tower_plan_line_view_action_view.xml", - "views/cx_tower_command_log_view.xml", - "views/cx_tower_plan_log_view.xml", - "views/cx_tower_key_view.xml", - "views/cx_tower_file_view.xml", - "views/cx_tower_file_template_view.xml", - "views/cx_tower_server_log_view.xml", - "views/cx_tower_server_template_view.xml", - "views/cx_tower_shortcut_view.xml", - "views/cx_tower_scheduled_task_view.xml", - "views/res_partner_view.xml", - "views/res_config_settings.xml", - "views/menuitems.xml", - ], - "demo": [ - "demo/demo_data.xml", - ], - "assets": { - "web.assets_backend": [ - "cetmix_tower_server/static/src/**/*.xml", - "cetmix_tower_server/static/src/**/*.js", - "cetmix_tower_server/static/src/**/*.scss", - ], - }, -} diff --git a/addons/cetmix_tower_server/data/ir_actions_server.xml b/addons/cetmix_tower_server/data/ir_actions_server.xml deleted file mode 100644 index 94b0c38..0000000 --- a/addons/cetmix_tower_server/data/ir_actions_server.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - Command - ir.actions.server - - - 10 - code - action = records.action_run_command() - - - - - Flight Plan - ir.actions.server - - - 12 - code - action = records.action_run_flight_plan() - - - - - Stop Flight Plan - ir.actions.server - - - 14 - code - action = records.action_stop() - - - diff --git a/addons/cetmix_tower_server/data/ir_config_parameter.xml b/addons/cetmix_tower_server/data/ir_config_parameter.xml deleted file mode 100644 index 4a0e985..0000000 --- a/addons/cetmix_tower_server/data/ir_config_parameter.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - cetmix_tower_server.command_timeout - 900 - - - cetmix_tower_server.notification_type_success - sticky - - - cetmix_tower_server.notification_type_error - sticky - - diff --git a/addons/cetmix_tower_server/data/ir_cron.xml b/addons/cetmix_tower_server/data/ir_cron.xml deleted file mode 100644 index 806ece1..0000000 --- a/addons/cetmix_tower_server/data/ir_cron.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - Cetmix Tower: Check zombie commands - - code - model._check_zombie_commands() - - 15 - minutes - -1 - - - - - - Cetmix Tower: Auto pull files from server - - code - model._run_auto_pull_files() - - 1 - days - -1 - - - - - - Cetmix Tower: Run scheduled tasks - - code - model._run_scheduled_tasks() - - 5 - minutes - -1 - - - - diff --git a/addons/cetmix_tower_server/data/neutralize.sql b/addons/cetmix_tower_server/data/neutralize.sql deleted file mode 100644 index e58ff8a..0000000 --- a/addons/cetmix_tower_server/data/neutralize.sql +++ /dev/null @@ -1,3 +0,0 @@ --- deactivate scheduled tasks -UPDATE cx_tower_scheduled_task - SET active = false; diff --git a/addons/cetmix_tower_server/demo/demo_data.xml b/addons/cetmix_tower_server/demo/demo_data.xml deleted file mode 100644 index d0d936a..0000000 --- a/addons/cetmix_tower_server/demo/demo_data.xml +++ /dev/null @@ -1,973 +0,0 @@ - - - - - - - - - - - - - Debian 10 - 1 - - - - Debian 11 - 1 - - - - Debian 12 - 1 - - - Ubuntu 20.04 - 2 - - - - Ubuntu 22.04 - 2 - - - - Ubuntu 24.04 - 2 - - - - - Staging - 1 - - - Production - 2 - - - Custom - 3 - - - - - Demo Key 1 SSH - k - Such Much SSH - - - Demo Key 2 Secret - s - Wow! Such much secret! - - - - - Demo Server #1 - 1 - stopped - localhost - admin - password - p - demohostkey - - demo1.example.com - - - - - This server is used in unit tests. - No variables are defined. - - - - 2 - Demo Server #2 - running - localhost - admin - password - k - p - True - - - https://dogememe.example.com - - - - This server has variables configured - - - cx.tower.server - - - - - - - Demo File Template 1 - tower_demo_1.txt - {{ demo_path }} - Hello, world! - - - - Demo File Template 2 - {{ demo_branch }}_tower_demo_2.txt - /tmp - Hello, world! - - - - Demo File Template 3 - tower_demo_3.txt - /tmp - How to create a directory: cd {{ demo_path }} && mkdir {{ demo_dir }} - - - - Demo File Template 4 - server - server_demo_logs.txt - /var/log - - - - - - tower_demo_1.txt - tower - - - - - tower_demo_2.txt - tower - - - - - tower_demo_3.txt - tower - - - - - server_demo_logs.txt - server - - - - - tower_demo_without_template_{{ demo_branch }}.txt - tower - - {{ demo_path }} - Please, check url: {{ demo_url }} - - - server_demo.txt - server - - {{ demo_path }} - - - - - Demo Path - demo_path - - - Demo Directory - demo_dir - - - Demo Operating System - demo_os - - - Demo URL - demo_url - - - Demo Odoo Version - demo_odoo_version - o - - - Demo Language - demo_language - o - - - Demo Version - demo_version - - - Demo Organisation - demo_org - - - Demo Branch - demo_branch - ^[a-z0-9]+$ - Must be lowercase and contain only letters and numbers! - - - - - - 14.0 - 14.0 - 10 - - - - 15.0 - 15.0 - 20 - - - - 16.0 - 16.0 - 30 - - - - 17.0 - 17.0 - 40 - - - - 18.0 - 18.0 - 50 - - - - - - English (US) - en_us - 10 - - - - Italian - it - 20 - - - - Spanish (Mexican) - es_mx - 30 - - - - German - de - 40 - - - - German (Switzerland) - de_ch - 50 - - - - - - - /home/{{ tower.server.username }}/tower/{{ demo_branch }} - - - - - /tmp/repo/{{ demo_branch }} - - - - - - - - - - - - - - - - - /opt/{{ tower.server.reference }}/cetmix-tower - - - - - /opt/{{ tower.server.reference }}/cetmix-tower - - - - - https://cetmix.com - - - - - staging - - - - - - - - - - - - - - - - prod - - - - Cetmix - - - - - - Demo Flight Plan #1 - Create directory and list its content - - 1 - - - - - - Demo Flight Plan #2 - Run another flight plan - - - - - Demo Flight Plan for User - Demo of a user-accessible flight plan - - 1 - - - - - Demo Flight Plan #4 - Update and upgrade packages - - - - - - Demo Flight Plan #5 - Check branch and download log file - - - - - - - Update packages - ssh_command - - apt-get update && apt-get upgrade -y - - - Update and upgrade packages on the host system - 1 - - - - - Create directory - ssh_command - - /home/{{ tower.server.username }} - mkdir -p {{ demo_dir }} - - Create a directory on the host system - 1 - - - - - List files in directory - ssh_command - /home/{{ tower.server.username }} - ls -l - 1 - - - List files in the directory - - - - - Upload file by template - file_using_template - /home/{{ tower.server.username }} - - - Upload a file to the host system - - - - - Run Python Code: Check Branch - python_code - -if {{ demo_branch }}: - result={"exit_code": 0, "message": "Branch is defined!"} -else: - result={"exit_code": -100, "message": "Branch is not defined!"} - - 1 - - Run Python Code: Check Branch - - - - - Download log file by template - file_using_template - /home/{{ tower.server.username }} - - - Download log file by template - - - - - Run Demo #1 Flight Plan - plan - - - - - - - 5 - - - {{ demo_path }} - - - - - - 1 - == - 0 - - - - 2 - > - 0 - ec - 255 - - - - - - - production - - - - - - - - - - 20 - - - {{ tower.server.status }} == 'running' and {{ demo_demo_odoo_version }} == "17.0" - - - - - - 1 - == - -1 - ec - 100 - - - - 2 - >= - 3 - n - - - - - 30 - - - - - - - 5 - - - - - - - 1 - - - {{ demo_path }} - - - - - 2 - - - {{ demo_path }} - - - - - 10 - - - - - - - 10 - - - - - - - 20 - - - - - - - tower_demo_1.txt - server - - - - - tower_demo_2.txt - server - - - - - - - Log from file - - file - - - - Log from file - - file - - - - - - Demo Server Template #1 - 1 - admin - password - p - - - - - - Log from file - - file - - - - - - - - /opt/{{ tower.server.reference }}/cetmix-tower/{{ demo_branch }} - - - - - https://cetmix.com - - - - - 1 - - - - - - 1 - - - - - Command Log for Server #1 - - command - - - - Command Log for Server #2 - - command - - - - Command Log for Server Template #1 - - command - - - - - - Demo Flight Plan Execution Status - demo_flight_plan_status_unique - - - Demo Flight Plan Start Time Unique - demo_flight_plan_start_time_unique - - - Demo Flight Plan End Time Unique - demo_flight_plan_end_time_unique - - - - - completed - - - - initial_value - - - - final_value - - - - - - - completed - - - - - initial_value - - - - - final_value - - - - - - - completed - - - - - initial_value - - - - - final_value - - - - - Command Shortcut - command - - - 1 - Runs a command. Use as an example to create your own shortcuts. - - - Flight Plan Shortcut - plan - - - 2 - Runs a flight plan. Use as an example to create your own shortcuts. - - - Flight Plan Shortcut for Server Template - plan - - - 2 - Runs a flight plan for a server template. Use as an example to create your own shortcuts. - - - - - Scheduled Task Demo #1 - scheduled_task_demo_1 - True - 1 - - - command - - 1 - hours - - - - Scheduled Task Demo #2 - scheduled_task_demo_2 - True - 2 - - plan - - 2 - days - - - - diff --git a/addons/cetmix_tower_server/i18n/cetmix_tower_server.pot b/addons/cetmix_tower_server/i18n/cetmix_tower_server.pot deleted file mode 100644 index e066700..0000000 --- a/addons/cetmix_tower_server/i18n/cetmix_tower_server.pot +++ /dev/null @@ -1,5000 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * cetmix_tower_server -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"Last-Translator: \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__source -msgid "" -"\n" -" - Tower: file is pushed from Tower to server.\n" -" - Server: file is pulled from server to Tower.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_user -msgid "" -"\n" -" Basic actions for selected servers.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_manager -msgid "" -"\n" -" Create and modify selected servers.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_root -msgid "" -"\n" -" Full control over all servers.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/constants.py:0 -#, python-format -msgid "" -"\n" -"

Help with Python expressions

\n" -"
\n" -"

\n" -" Each Python code command returns the result value which is a dictionary.\n" -"
There are two keys in the dictionary:\n" -"

    \n" -"
  • exit_code: Integer. Exit code of the command. \"0\" means success, any other value means failure. Default value is \"0\".
  • \n" -"
  • message: String. Message to be logged. Default value is \"None\".
  • \n" -"
\n" -"You can also access the custom_values dictionary that contains custom values provided to the command or flight plan.\n" -"Custom values can be modified, thus can be used to pass data between commands in a flight plan.\n" -"Please keep in mind that custom values are persistent only between commands in a flight plan and are not saved to the database.\n" -"
\n" -"Here is an example of a python code command:\n" -"\n" -"\n" -" server_name = server.name\n" -" build_name = custom_values.get(\"build_name\")\n" -" if build_name:\n" -" result = {\"exit_code\": 0, \"message\": \"Build name for \" + server_name + \" is \" + build_name}\n" -" else:\n" -" result = {\"exit_code\": 0, \"message\": \"No build name provided for \" + server_name}\n" -" custom_values[\"build_name\"] = \"New build name\"\n" -"\n" -"

\n" -"
\n" -"Please refer to the official documentation for more information and examples.\n" -"
\n" -"Various fields may use Python code or Python expressions. The\n" -" following variables can be used:

\n" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Empty values for variables: %(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Missing variables: %(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/constants.py:0 -#, python-format -msgid "" -"# Please refer to the 'Help' tab and documentation for more information.\n" -"#\n" -"# You can return command result in the 'result' variable which is a dictionary:\n" -"# result = {\"exit_code\": 0, \"message\": \"Some message\"}\n" -"# default value is {\"exit_code\": 0, \"message\": None}\n" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_reference_mixin.py:0 -#, python-format -msgid "%(name)s (copy %(number)s)" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_reference_mixin.py:0 -#, python-format -msgid "%(name)s (copy)" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_shortcut.py:0 -#, python-format -msgid "%(shr)s triggered" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command_log.py:0 -#, python-format -msgid "%(timestamp)s
Command '%(name)s' finished successfully" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command_log.py:0 -#, python-format -msgid "%(timestamp)s
Command '%(name)s' finished with error" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "%(timestamp)s
Flight Plan '%(name)s' finished successfully" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "%(timestamp)s
Flight Plan '%(name)s' finished with error" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "...save record to see the final expression or click the line to edit" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__command_status -msgid "" -"0 if command finished successfully.\n" -"-100 general error,\n" -"-101 not found,\n" -"-201 another instance of this command is running,\n" -"-202 no runner found for the command action,\n" -"-203 Python code execution failed\n" -"-205 plan line condition check failed\n" -"503 if SSH connection error occurred\n" -"601 if queue job failed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__plan_status -msgid "" -"0 if plan is finished successfully. \n" -"-301 if another instance of this flight plan is running, \n" -"-302 if plan is empty, \n" -"-303 if plan reference is missing, \n" -"-304 if plan line reference is missing, \n" -"-306 if plan is not compatible with server,\n" -"-308 if plan is stopped by user" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "AND" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "" -"\n" -" &nbsp;" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_log.py:0 -#, python-format -msgid "" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command_log.py:0 -#, python-format -msgid "" -"

Error converting command response to HTML: %(error)s

" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "" -"sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "" -"Fill in required configuration variables on the “Configuration Values”" -" tab before you can run the command." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "IPv4 Address:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "IPv6 Address:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Operating System:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Partner:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -msgid "Servers:" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "A helper shortcut to env['cx.tower.command']" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "A helper shortcut to env['cx.tower.plan']" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "A helper shortcut to env['cx.tower.server']" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_action -msgid "" -"A variable value cannot be assigned multiple times to the same plan line " -"action!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_template -msgid "" -"A variable value cannot be assigned multiple times to the same server " -"template!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_server -msgid "A variable value cannot be assigned multiple times to the same server!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Access" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__access_level -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Access Level" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level_warn_msg -msgid "Access Level Warn Msg" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "Access level is not defined for '%(option)s'" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Access level is not defined for '%(variable)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__action -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction -msgid "Action Needed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "" -"Actions trigger based on command result. If empty next command will be " -"executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__active -msgid "Active" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_ids -msgid "Activities" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "Activity State" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Activity Type Icon" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_action -msgid "Add a new file" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_template_action -msgid "Add a new file template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__note -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__note -msgid "" -"Additional notes about the variable. \n" -"This field will be displayed in the variable form." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -msgid "All" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Allow Parallel Run" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "An error occurred: %(error)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Another instance of the command is already running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__applicability -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__applicability -msgid "Applicability" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__applied_expression -msgid "Applied Expression" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Archived" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Are you sure?" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_attachment_count -msgid "Attachment Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__auto_sync -msgid "Auto Sync" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync_interval -msgid "Auto Sync Interval" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__value_char -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__value_char -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__value_char -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task_cv__value_char -msgid "" -"Automatically populated from selected option. Manual edits will be " -"overwritten when option changes." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_automation_root -msgid "Automation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file -msgid "Binary Content" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key_value__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_shortcut__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_view_form -msgid "" -"Can contain English letters, digits and '_'. Leave blank to autogenerate" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Cancel" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for '%(var)s' with value '%(val)s'.\n" -"Try to assigns it to a record instead." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for 'meme' with value 'Pepe'.\n" -"Try to assigns it to a record instead." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_tag.py:0 -#, python-format -msgid "" -"Cannot delete tag '%(tag_name)s' because it is used in related records." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Cannot download %(f)s from server: Binary content is not supported for " -"'Text' file type" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Cannot pull %(f)s from server: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot remove test file using command.\n" -" CODE: %(status)s. ERROR: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot run command\n" -". CODE: %(status)s. RESULT: %(res)s. ERROR: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower -#: model:ir.ui.menu,name:cetmix_tower_server.menu_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_res_partner_form_inherit_cetmix_tower -msgid "Cetmix Tower" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "" -"Cetmix Tower helper class shortcut" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command -msgid "Cetmix Tower Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_log -msgid "Cetmix Tower Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file -msgid "Cetmix Tower File" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file_template -msgid "Cetmix Tower File Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan -msgid "Cetmix Tower Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line -msgid "Cetmix Tower Flight Plan Line" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line_action -msgid "Cetmix Tower Flight Plan Line Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_log -msgid "Cetmix Tower Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key_mixin -msgid "Cetmix Tower Key/Secret Mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key -msgid "Cetmix Tower Key/Secret Storage" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cetmix_tower -msgid "Cetmix Tower Odoo Automation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_os -msgid "Cetmix Tower Operating System" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_command_run_wizard_action -msgid "Cetmix Tower Run Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_plan_run_wizard_action -msgid "Cetmix Tower Run Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key_value -msgid "Cetmix Tower Secret Value Storage" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server -msgid "Cetmix Tower Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_log -msgid "Cetmix Tower Server Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template -msgid "Cetmix Tower Server Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_shortcut -msgid "Cetmix Tower Shortcut" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_tag -msgid "Cetmix Tower Tag" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_tag_mixin -msgid "Cetmix Tower Tag Mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable -msgid "Cetmix Tower Variable" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_option -msgid "Cetmix Tower Variable Options" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_value -msgid "Cetmix Tower Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_vault -msgid "Cetmix Tower Vault" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_vault_mixin -msgid "Cetmix Tower Vault Mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_mixin -msgid "Cetmix Tower access mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_role_mixin -msgid "Cetmix Tower access role mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_reference_mixin -msgid "Cetmix Tower reference mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_template_mixin -msgid "Cetmix Tower template rendering mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_auto_pull_files_from_server_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_auto_pull_files_from_server -msgid "Cetmix Tower: Auto pull files from server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_check_zombie_commands_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_check_zombie_commands -msgid "Cetmix Tower: Check zombie commands" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_run_scheduled_tasks_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_run_scheduled_tasks -msgid "Cetmix Tower: Run scheduled tasks" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_shortcut.py:0 -#, python-format -msgid "Check %(t)s log for result" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -msgid "" -"Check the key before inserting in the server settings. Do not insert the key" -" if you have any doubts!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Child plans" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-javascript -#: code:addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -#, python-format -msgid "Close" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Code On Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__template_code -msgid "Code of the associated file template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__color -msgid "Color" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_command -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__command_ids -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__action__command -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_shortcut__action__command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Command" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "Command %(command_name)s is not compatible with the server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__code -msgid "Command Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__command_ids_count -msgid "Command Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__command_domain -msgid "Command Domain" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__command_help -msgid "Command Help" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command_log -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__command_log_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__command_log_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_log -#, python-format -msgid "Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Command Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_result_html -msgid "Command Result Html" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_config_settings__cetmix_tower_command_timeout -msgid "Command Timeout" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__command_variable_ids -msgid "Command Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__code -msgid "Command code that was executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__is_running -msgid "Command is being executed right now" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Command is not compatible with the server" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Command not found" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__command_id -msgid "" -"Command that will be executed to get the log data.\n" -"Be careful with commands that don't support parallel execution!" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/constants.py:0 -#, python-format -msgid "Command timed out and was terminated" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "" -"Command timeout in seconds after which the command will be terminated. Set to 0 to disable timeout.\n" -"
" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__command_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__plan_line_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__command_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__command_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__command_ids -msgid "Commands used in this flight plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__condition -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Condition" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__condition -msgid "" -"Conditions under which this Flight Plan Line will be launched. e.g.: {{ " -"odoo_version}} == '14.0'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_res_config_settings -msgid "Config Settings" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Configuration" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Configuration Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__line_ids -msgid "Configuration Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Confirm" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection failed." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection successful." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Connection test passed! \n" -"%(res)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_res_partner -msgid "Contact" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#, python-format -msgid "Create Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard -msgid "Create new server from template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard_line -msgid "Create new server from template variables" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__create_uid -msgid "Created by" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__create_date -msgid "Created on" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/res_config_settings.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -#, python-format -msgid "Cron Job" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/res_config_settings.py:0 -#, python-format -msgid "Cron job not found" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Current Cetmix Tower server this command is running on" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Current Odoo user" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Current Odoo user ID" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Custom Exit Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__custom_message -msgid "Custom Message" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__custom_variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__custom_variable_value_ids -msgid "Custom Variable Value" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__custom_variable_value_ids -msgid "Custom Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Custom label. Can be used for search/tracking" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__custom_message -msgid "Custom message to be displayed in the plan log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Custom message to display when pattern check fails" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Custom secret values can be defined only for key type 'secret'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -msgid "" -"Custom values will be available in the current flight plan context only. " -"They can be used in Python commands and line conditions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_custom_variable_value_mixin -msgid "Custom variable values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_run_wizard_variable_value -msgid "Custom variable values for command run wizard" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_run_wizard_variable_value -msgid "Custom variable values for plan run wizard" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_scheduled_task_cv -msgid "Custom variable values for scheduled tasks" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__variable_values -msgid "Custom variable values passed to the command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__variable_values -msgid "Custom variable values passed to the flight plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Date and time of the latest successful synchronisation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Date and time of the next synchronisation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__days -msgid "Days" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__path -msgid "Default Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "Default full file name with file type for example: test.txt" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_delete_action -msgid "Delete from server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_dir -msgid "Directory on Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__server_dir -msgid "Directory on server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__disconnect_file -msgid "Disconnect from Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__display_name -msgid "Display Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__skip_host_key -msgid "Don't Check Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_download_action -msgid "Download" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Due to security restrictions you are not allowed to delete %(fp)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Duration" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "Duration, sec" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_vault_vault_unique_key -msgid "Each secret (model, record, field) must be unique in the vault." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_dir -msgid "Eg '/home/user' or '/var/log'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__skip_host_key -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__skip_host_key -msgid "Enable to skip host key verification" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Enter Python code here. Help about Python expression is available in the " -"help tab of this document." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_error -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Error" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_config_settings__cetmix_tower_notification_type_error -msgid "Error Notifications" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Error retrieving host key: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "" -"Example:\n" -"
\n" -" \n" -" result = value.lower().strip().replace('_', '-').replace(\" \",\"\") if value.startswith('http') else 'https://' + re.sub(r'\\s+', '', value)\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Execution Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_status -msgid "Exit Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__e -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__e -msgid "Exit with command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__ec -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__ec -msgid "Exit with custom exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Failed" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect after %(attempts)s attempts. Error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect. Error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#, python-format -msgid "Failure" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__field_name -msgid "Field Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__file_ids -msgid "File" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"File %(f)s is not 'tower' type. This operation is supported for 'tower' " -"files only" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"File %(f)s shouldn't have the '%(src)s' source for the '%(act)s' action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__file_ids_count -msgid "File Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "File Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__file_template_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "File Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__file_template_ids_count -msgid "File Template Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__file_template_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "File Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "File Type" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File already exists" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file_template.py:0 -#, python-format -msgid "File already exists on server." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File already exists on server. Upload skipped" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__code -msgid "File content" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "File content with variables rendered" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File created and uploaded successfully" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File deleted!" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File downloaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__name -msgid "File name WITHOUT path. Eg 'test.txt'" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File source cannot be determined: '%(source)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_id -msgid "File that will be executed to get the log data" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File uploaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "File will be disconnected from template. Continue?" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "File will be kept on server when deleted in Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_count -msgid "File(s)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Files" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files deleted!" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files downloaded!" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files uploaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "" -"Files will be pulled from server to Tower\n" -" automatically using cron job.\n" -"
" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Finish date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__finish_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__finish_date -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Finished" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_plan -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__plan_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__action__plan -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_shortcut__action__plan -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_run_line_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Flight Plan Lines" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan_log -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan_log -msgid "Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Flight Plan Logs" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "Flight Plan Stopped" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_used_ids -msgid "Flight Plan Used" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_used_ids_count -msgid "Flight Plan Used Ids Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Flight Plan line that is being currently executed" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "" -"Flight Plan will be terminated after executing the current command. " -"Continue?" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan -msgid "Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "Flight Plans Stopped" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "Flight plan is not compatible with the server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__flight_plan_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__plan_run_id -msgid "Flight plan run by the command" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Flight plan running error" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Flight plan running error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__flight_plan_used_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__flight_plan_used_ids_count -msgid "Flight plan this command is used in" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__is_stopped -msgid "Flight plan was stopped by user" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Float compare. Odoo helper function to compare floats." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_follower_ids -msgid "Followers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_partner_ids -msgid "Followers (Partners)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Font awesome icon e.g. fa-tasks" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__color -msgid "For better visualization in views" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "For how long a flight plan is already running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_run_wizard__applicability__this -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_run_wizard__applicability__this -msgid "For selected server(s)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__full_server_path -msgid "Full Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cetmix_tower_config_settings -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cetmix_tower_general_settings -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "General Settings" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Get from Host" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__is_global -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__is_global -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Global" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Group By" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/common.py:0 -#: code:addons/cetmix_tower_server/tests/common.py:0 -#: code:addons/cetmix_tower_server/tests/common.py:0 -#: code:addons/cetmix_tower_server/tests/common.py:0 -#, python-format -msgid "Group reference %s not found!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__has_message -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__has_message -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__has_message -msgid "Has Message" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__has_missing_required_values -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__has_missing_required_values -msgid "Has Missing Required Values" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Has Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__have_access_to_server -msgid "Have Access To Server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Help" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard.py:0 -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__host_key -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__host_key -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__host_key -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -#, python-format -msgid "Host Key" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Host key is missing!" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Host key not found for server %(server)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__host_key -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__host_key -msgid "Host key to verify the server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__hours -msgid "Hours" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -msgid "" -"I confirm that the key is correct and I want to insert it in the server " -"settings" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__id -msgid "ID" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_vault__res_id -msgid "ID of the resource that uses this vault" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v4_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v4_address -msgid "IPv4 Address" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v6_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v6_address -msgid "IPv6 Address" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__if_file_exists -msgid "If File Exists" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction -msgid "If checked, new messages require your attention." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "If checked, some messages have a delivery error." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__auto_sync -msgid "If enabled file will be synced automatically using cron" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__disconnect_file -msgid "" -"If enabled, disconnects the file from its template after running the " -"command.\n" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__no_split_for_sudo -msgid "" -"If enabled, do not split command on '&&' when using sudo.Prepend sudo once " -"to the whole command." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__auto_sync -msgid "" -"If enabled, files created from this template will have Auto Sync enabled by " -"default. Used only with 'Tower' source." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -msgid "" -"If enabled, multiple instances of the same command can be run on the same server at the same time.\n" -"Otherwise, ANOTHER_COMMAND_RUNNING status will be returned if another instance of the same command is already running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -msgid "" -"If enabled, multiple instances of the same flight plan can be run on the same server at the same time.\n" -"Otherwise, ANOTHER_PLAN_RUNNING status will be returned if another instance of the same flight plan is already running" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#, python-format -msgid "If exit code" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/test_plan.py:0 -#, python-format -msgid "If exit code == 35 then Exit with command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "" -"In the Secure Shell (SSH) protocol, host keys are used to verify the " -"identity of remote hosts. Accepting unknown host keys may leave the " -"connection open to man-in-the-middle attacks." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -msgid "Indicates if this variable is mandatory for server creation" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -msgid "Insert Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__interval_number -msgid "Interval Number" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -msgid "Interval Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__interval_type -msgid "Interval Unit" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_scheduled_task_interval_positive -msgid "Interval number must be greater than zero." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#, python-format -msgid "Invalid value!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__is_error -msgid "Is Error" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_is_follower -msgid "Is Follower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_running -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__is_running -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__is_running -msgid "Is Running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_skipped -msgid "Is Skipped" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "Keep When Deleted" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__key_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__k -msgid "Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__key_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Key Type" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Key Value" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard.py:0 -#, python-format -msgid "Key inserted successfully!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Key reference for inline usage" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_key -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_key_root -msgid "Keys and Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Label" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Labeled" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__last_call -msgid "Last Execution Date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault____last_update -msgid "Last Modified on" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Last Sync Date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__write_uid -msgid "Last Updated by" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__write_date -msgid "Last Updated on" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Latest version of file content on server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__line_id -msgid "Line" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_line_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__line_ids -msgid "Lines" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__flight_plan_line_ids -msgid "Lines of the associated flight plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Local" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "" -"Location where command will be executed. Overrides command default path. You" -" can use {{ variables }} in path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__path -msgid "" -"Location where command will be run. You can use {{ variables }} in path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_html -msgid "Log Html" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_text -msgid "Log Text" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_type -msgid "Log Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_log_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_main_attachment_id -msgid "Main Attachment" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__parent_flight_plan_log_id -msgid "Main Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plans" -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_manager -msgid "Manager" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__manager_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Managers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers who can modify this record" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "Message Delivery error" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__validation_message -msgid "" -"Message to display when the variable value is invalid. \n" -"First line will be added automatically: `Variable:, Value: `\n" -"Eg: `Variable: Customer Name, Value: Test\n" -"Invalid value!`\n" -"If empty, the default message will be used." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_ids -msgid "Messages" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__minutes -msgid "Minutes" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables -msgid "Missing Required Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__missing_required_variables_message -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables_message -msgid "Missing Required Variables Message" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_vault__res_model -msgid "Model name of the resource that uses this vault" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Modify Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__months -msgid "Months" -msgstr "" - -#. module: cetmix_tower_server -#: model:cx.tower.variable,validation_message:cetmix_tower_server.variable_demo_branch -msgid "Must be lowercase and contain only letters and numbers!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__name -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_vault__field_name -msgid "Name of the field that contains the secret value" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -msgid "Name/Reference" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_summary -msgid "Next Activity Summary" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_id -msgid "Next Activity Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__next_call -msgid "Next Execution Date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Next Sync Date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__next_call -msgid "Next planned execution date for this task." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "No" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__no_split_for_sudo -msgid "No Split for sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Synced" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Template" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"No output received. Please log in manually and check for any issues.\n" -"===\n" -"CODE: %(status)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "No runner found for command action '%(cmd_action)s'" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-javascript -#: code:addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml:0 -#, python-format -msgid "No secrets found" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "No server found for the provided reference." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-javascript -#: code:addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml:0 -#, python-format -msgid "No variables found" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "No/Undefined" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_run_wizard__applicability__shared -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_run_wizard__applicability__shared -msgid "Non server restricted" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/res_config_settings.py:0 -#, python-format -msgid "Non-sticky" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Not Running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__note -msgid "Note" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "Notifications" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of errors" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of messages with delivery error" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_os -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "OS" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "" -"OS %(os)s used by the server '%(srv)s' is not present in the command's OS " -"compatibility list" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__os_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "OSes" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Odoo Environment" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_delete_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "On Delete Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "On Error" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Only one global secret value can be defined for a key" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Only one global value can be defined for variable '%(var)s'" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "Only one global value can be defined for variable 'meme'" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Only one secret value can be defined for a partner" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Only one secret value can be defined for a server" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Only one secret value can be defined for a server and partner" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Only values that the current user has access to are shown." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open full form" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__os_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__os_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__os_id -msgid "Operating System" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_os -msgid "Operating Systems" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__option_id -msgid "Option" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Option '%(val)s' is not available for variable '%(var)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__option_ids -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__o -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Options" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__os_compatibility_warning -msgid "Os Compatibility Warning" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command__if_file_exists__overwrite -msgid "Overwrite" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__partner_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__partner_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__partner_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Partner" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key_value__partner_id -msgid "Partner to which the key belongs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_cetmix_tower_partner -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_partner -msgid "Partners" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__p -msgid "Password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__path -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__plan_domain -msgid "Plan Domain" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__plan_line_ids -msgid "Plan Line" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__plan_line_action_id -msgid "Plan Line Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__plan_line_ids_count -msgid "Plan Line Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Plan Line Executed" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -#, python-format -msgid "Plan Lines" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard.py:0 -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__plan_log_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_log_ids -#, python-format -msgid "Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__is_running -msgid "Plan is being executed right now" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Plan line condition check failed." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__plan_ids -msgid "Plans" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide IPv4 or IPv6 address for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH Key for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH password for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py:0 -#, python-format -msgid "" -"Please provide values for the following configuration variables: " -"%(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "" -"Please provide values for the following configuration variables: %(vars)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "Please resolve the following issues with configuration variables:" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Please select a command to execute" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Post Run Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Preview" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__parent_id -msgid "Previous Version" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__last_call -msgid "Previous time the task ran successfully." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_property_root -msgid "Properties" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "Pull files from server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Pull from Server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Push to Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__path -msgid "" -"Put custom path to run the command.\n" -"IMPORTANT: this field does NOT support variables!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_view_form -msgid "Put your notes here" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Put your notes here..." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'datetime' library" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'dateutil' library" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "" -"Python 'dnspython' library. Documentation.
  • dns.resolver: wrapped" -" dnspython. Use dns.resolver.resolve(hostname, \"A\") for DNS " -"lookups.
  • dns.reversename: wrapped dnspython. Use " -"dns.reversename.from_address(\"8.8.8.8\") to build and reverse " -"PTR records.
  • dns.exception: wrapped dnspython. Catch " -"dns.exception.DNSException to handle DNS-related " -"errors.
" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "" -"Python 'hashlib' library. Documentation. Available methods: 'sha1', 'sha224', " -"'sha256', 'sha384', 'sha512', 'sha3_224', 'sha3_256', 'sha3_384', " -"'sha3_512', 'shake_128', 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "" -"Python 'hmac' library. Documentation. Use 'new' to create HMAC objects. " -"Available methods on the HMAC *object*: 'update', 'copy', 'digest', " -"'hexdigest'. Module-level function: 'compare_digest'." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'json' library. Available methods: 'dumps'" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "" -"Python 'requests' library. Available methods: 'post', 'get', 'delete', " -"'request'" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'time' library" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'timezone' library" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "" -"Python 'tldextract' library. Use tldextract.extract() to parse " -"domains. Check tldextract for more information." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_run_wizard__action__python_code -msgid "Python code" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Python code running error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "" -"Python code that is used to modify the variable values.\n" -"
\n" -" Available variables and functions:" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__applied_expression -msgid "" -"Python expression to apply to the variable value. \n" -"You can use general python sting functions and 're' module for regex operations. Use 'value' variable to refer to the variable value, use 'result' to assign the final result that will be used as a variable value.\n" -"Eg 'result = value.lower().replace(' ', '_')'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command__if_file_exists__raise -msgid "Raise Error" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Recursive plan call detected in plan %(name)s." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__reference -msgid "Reference" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Reference Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_command_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_file_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_file_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_project_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_project_rel_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_remote_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_repo_owner_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_repo_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_source_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_key_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_key_value_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_os_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_action_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_reference_mixin_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_scheduled_task_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_log_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_shortcut_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_tag_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_webhook_authenticator_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_webhook_eval_mixin_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_webhook_reference_unique -msgid "Reference must be unique" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "" -"Reference. Can contain English letters, digits and '_'. Leave blank to " -"autogenerate" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -msgid "Refresh" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Refresh All" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Regex expression, eg. ^[a-z0-9]+$" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__validation_pattern -msgid "" -"Regex pattern to validate the variable values using the 're.match' function. Eg. ^[a-z0-9]+$ \n" -"If empty, the variable values will not be validated." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote\n" -" one." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote " -"one." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__rendered_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "Rendered Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_name -msgid "Rendered Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_server_dir -msgid "Rendered Server Dir" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__interval_number -msgid "Repeat every x." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__required -msgid "Required" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__res_id -msgid "Resource ID" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__res_model -msgid "Resource Model" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_response -msgid "Response" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_user_id -msgid "Responsible User" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__result -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Result" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__command_result_html -msgid "Result converted to HTML. Used for SSH commands." -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_root -msgid "Root" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "Run" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#, python-format -msgid "Run Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_run_wizard -msgid "Run Command in Wizard" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_run_id -#, python-format -msgid "Run Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_run_wizard -msgid "Run Flight Plan in Wizard" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Run Manually" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Run New Command" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "Run Plan" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Run Result" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "" -"Run code as it appears in 'Rendered code' in wizard and return to wizard. " -"Result will not be logged" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Run code using server method and log result" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_shortcut__use_sudo -msgid "Run command using 'sudo'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__use_sudo -msgid "Run commands using 'sudo'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__use_sudo -msgid "Run commands using 'sudo'. Leave empty if 'sudo' is not needed." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Run in wizard" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__n -msgid "Run next command" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "Run on" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "Run scheduled tasks" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Running" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Running Now" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_auth_mode -msgid "SSH Auth Mode" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH Client is not defined." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__k -msgid "SSH Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_key -msgid "SSH Key / Secret" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_password -msgid "SSH Password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__secret_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_key_id -msgid "SSH Private Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "SSH Username" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_run_wizard__action__ssh_command -msgid "SSH command" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH connection error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_port -msgid "SSH port" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH run command error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_scheduled_task -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__scheduled_task_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__scheduled_task_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__scheduled_task_id -msgid "Scheduled Task" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_scheduled_task -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__scheduled_task_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__scheduled_task_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_scheduled_task -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Scheduled Tasks" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__scheduled_task_id -msgid "Scheduled task that triggered this command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__scheduled_task_id -msgid "Scheduled task that triggered this flight plan" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#, python-format -msgid "Scheduled tasks run successfully." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "" -"Scheduled tasks will be run automatically using cron job.\n" -"
" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Search Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Search Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -msgid "Search File Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Search Files" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Search Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Search Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Search Keys/Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_search_view -msgid "Search OS" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -msgid "Search Scheduled Tasks" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Search Server Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Search Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Search Shortcuts" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_search_view -msgid "Search Tags" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Search Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__s -msgid "Secret" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__data -msgid "Secret Data" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__secret_value -msgid "Secret Value" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Secret Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_partner__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_users__secret_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_res_partner_form_inherit_cetmix_tower -msgid "Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__applicability -msgid "" -"Selected server(s): only Commands that are specific to the selected server(s)\n" -"Non server restricted: all Commands that are not specific to any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_run_wizard__applicability -msgid "" -"Selected server(s): only Flight Plans that are specific to the selected server(s)\n" -"Non server restricted: all Flight Plans that are not specific to any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__sequence -msgid "Sequence" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_ir_actions_server -msgid "Server Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_partner__server_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_users__server_count -msgid "Server Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_log_ids -msgid "Server Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_log_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__name -msgid "Server Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_response -msgid "Server Response" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_status -msgid "Server Status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Server Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server_template -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__server_template_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__server_template_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__server_template_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_view_form -msgid "Server Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server URL, eg 'https://meme.example.com'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Server Version" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Server not found" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_response -msgid "" -"Server response received during the last operation.\n" -"Default value if no error happened is 'ok'.\n" -"Otherwise there will be a server error message logged." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Server tight" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key_value__server_id -msgid "Server to which the key belongs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__url -msgid "Server web interface, eg 'https://doge.example.com'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_partner__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_users__server_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_res_partner_filter -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_res_partner_form_inherit_cetmix_tower -msgid "Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Servers (SSH)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_ids -msgid "" -"Servers on which the command will be run.\n" -"If empty, command can be run on all servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -msgid "Set Custom Values" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Set Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_status -msgid "" -"Set the following status if command finishes with success. Leave 'Undefined'" -" if you don't need to update the status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_settings -msgid "Settings" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_shortcut -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__shortcut_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__shortcut_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_shortcut -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Shortcuts" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Show Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "Show Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_host_key_wizard -msgid "Show Host Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__show_servers -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__show_servers -msgid "Show Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "" -"Show notifications for error events. Select \"Sticky\" to keep the " -"notification visible until dismissed. Leave empty to disable notifications." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "" -"Show notifications for success events. Select \"Sticky\" to keep the " -"notification visible until dismissed. Leave empty to disable notifications." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command__if_file_exists__skip -msgid "Skip" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__skip_host_key -msgid "Skip Host Key" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Skipped" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Some servers don't support this command" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Some variable options are invalid:\n" -"%(detailed_message)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__source -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__source -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Source" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Start date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__start_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__start_date -msgid "Started" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_status -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__status -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "" -"Status based on activities\n" -"Overdue: Due date is already passed\n" -"Today: Activity date is today\n" -"Planned: Future activities." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/res_config_settings.py:0 -#, python-format -msgid "Sticky" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Stop" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.action_stop_cx_tower_plan_log -msgid "Stop Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__is_stopped -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Stopped" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command_log.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "Stopped by user %(user)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__s -msgid "String" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#, python-format -msgid "Success" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_config_settings__cetmix_tower_notification_type_success -msgid "Success Notifications" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Sync Error" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Synced" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "System Settings" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_tag -msgid "Tag" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Tagged" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag_mixin__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__tag_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_tag -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -msgid "Tags" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__template_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Template Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_template_action -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_template -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_template -msgid "Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Test Connection" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "" -"The access level for Variable Option '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Option Access Level: %(val_level)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"The access level for Variable Value '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Value Access Level: %(val_level)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "" -"The access level of command(s) '%(command_names)s' included in the current " -"Flight plan is higher than the access level of the Flight plan itself. " -"Please ensure that you want to allow those commands to be run anyway." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_unique_variable_option_name -msgid "The combination of Name and Variable must be unique." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_unique_variable_option -msgid "The combination of Value and Variable must be unique." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "The file %(f_path)s not found." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_vault__data -msgid "The secret data to be stored in the vault" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#, python-format -msgid "" -"The selected task interval is too low in relation to the general system " -"settings. This may lead to task execution delays." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Then" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "This Flightplan will be executed when the server is deleted" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__plan_delete_id -msgid "This Flightplan will be run when the server is deleted" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "" -"This action will be triggered on error if no command action can be applied" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "This command can be used only in Flight Plans." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__note -msgid "This field is used to put some notes regarding template." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_template_mixin__code -msgid "This field will be rendered using variables" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_template_id -msgid "" -"This file template will be used to create log files when server is created " -"from a template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -msgid "This flight plan will be run upon server creation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "" -"This is required, however you can change this later in the server settings" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -msgid "This template will be used to create or update the pushed file" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key_value__is_global -msgid "This value is applicable to all servers and partners" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Time consumed for execution, seconds" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_res_config_settings__cetmix_tower_command_timeout -msgid "" -"Timeout for commands in seconds after which the command will be terminated" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_tools -msgid "Tools" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_count -msgid "Total Files" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__tower -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__tower -msgid "Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_mixin -msgid "Tower Variables mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Trigger shortcut" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Triggered Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__triggered_plan_command_log_ids -msgid "Triggered Flight Plan Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__triggered_plan_log_id -msgid "Triggered Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_type -msgid "Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_res_config_settings__cetmix_tower_notification_type_error -msgid "Type of error notifications" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_res_config_settings__cetmix_tower_notification_type_success -msgid "Type of success notifications" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Type of the exception activity on record." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__url -msgid "URL" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to delete file '%(f)s'.\n" -"Delete operation is not supported for 'server' type files." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#, python-format -msgid "Unable to run scheduled task '%(f)s'. Error: %(e)s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to upload file '%(f)s'.\n" -"Upload operation is not supported for 'server' type files." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_upload_action -msgid "Upload" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__use_sudo -msgid "Use Sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__use_sudo -msgid "Use sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH key in the following servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Used for" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Used in Plans" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Used in Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_user -msgid "User" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "UserError. Helper to raise UserError." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__user_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Users" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users who can view this record" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__validation_message -msgid "Validation Message" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__validation_pattern -msgid "Validation Pattern" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Value" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids_count -msgid "Value Count" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Value Modifier" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py:0 -#, python-format -msgid "Value is invalid" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Variable" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Variable '%(var)s' can only be assigned to one of the models at a time: " -"Server, Server Template, or Plan Line Action." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -msgid "Variable Reference" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -msgid "Variable Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_value_ids -msgid "Variable Value" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_value_ids_count -msgid "Variable Value Count" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__variable_values -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__variable_values -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable_value -#, python-format -msgid "Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_tower_variable_value_uniq -msgid "Variable can be declared only once for the same record!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_name_uniq -msgid "Variable names must be unique" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable not found" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Variable reference '%(var_ref)s' has an invalid option reference " -"'%(opt_ref)s'." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_template_mixin.py:0 -#, python-format -msgid "Variable syntax error: %s" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value created" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value updated" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -msgid "Variable values for selected record" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#, python-format -msgid "" -"Variable: %(var)s, Value: %(val)s\n" -"%(msg)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable_root -msgid "Variables" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command_log.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "View Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__warning_message -msgid "Warning Message" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__os_compatibility_warning -msgid "Warning about OS compatibility of the command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__weeks -msgid "Weeks" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__if_file_exists -msgid "" -"What to do if file already exists on the server.\n" -"- Skip: Do not create or update the file.\n" -"- Overwrite: Replace the existing file with the new one.\n" -"- Raise Error: Raise an error if the file already exists." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Where command was executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Will be used instead of the command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__use_sudo -msgid "" -"Will use sudo based on server settings.If no sudo is configured will run " -"without sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_res_partner_filter -msgid "With Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__use_sudo__p -msgid "With password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__use_sudo__n -msgid "Without password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__wizard_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__wizard_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__wizard_id -msgid "Wizard" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "Wrong action" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "You are not allowed to execute commands in wizard" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_log.py:0 -#, python-format -msgid "You are not allowed to modify the server log output." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "You cannot execute an empty command" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "You cannot run custom code on multiple servers at once." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "" -"You need 'Manager' access to the server to override the default configuration values.\n" -" Without this access, the server's configured values will be used." -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/ir_actions_server.py:0 -#, python-format -msgid "" -"You need to have 'write' access to all servers you want to run this action " -"on." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "e.g. /home/user This field does NOT support variables" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "e.g. /such/much/{{ path }}, overrides command path" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "general python functions (eg. lower(), replace(), etc.)" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/common.py:0 -#: code:addons/cetmix_tower_server/tests/common.py:0 -#, python-format -msgid "groups_ref must be string or list of strings!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "managers who can modify this record" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "managers who can modify this server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "managers who can modify this template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "new server name" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "optional, eg /home/{{ tower.server.username }}" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "re: regex operations (eg. re.sub, re.match, etc.)" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "result: final result that will be used as a variable value" -msgstr "" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "then" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "this can be changed later" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "undefined" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "users who can access this server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "users who can access this template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "users who can view this record" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "value: variable value" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "with tags" -msgstr "" diff --git a/addons/cetmix_tower_server/i18n/de.po b/addons/cetmix_tower_server/i18n/de.po deleted file mode 100644 index 202d0fe..0000000 --- a/addons/cetmix_tower_server/i18n/de.po +++ /dev/null @@ -1,3462 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * cetmix_tower_server -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: weblatereports@cetmix.com\n" -"PO-Revision-Date: 2025-03-03 12:07+0000\n" -"Last-Translator: CetmixWeblateBot \n" -"Language-Team: German \n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.10.3-dev\n" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__source -msgid "" -"\n" -" - Tower: file is pushed from Tower to server.\n" -" - Server: file is pulled from server to Tower.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_user -msgid "" -"\n" -" Basic actions for selected servers.\n" -" " -msgstr "" -"\n" -" Basisaktionen für die ausgewählten Server.\n" -" " - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_manager -msgid "" -"\n" -" Create and modify selected servers.\n" -" " -msgstr "" -"\n" -" Erstelle und editiere den gewählten Server.\n" -" " - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_root -msgid "" -"\n" -" Full control over all servers.\n" -" " -msgstr "" -"\n" -" Alle Rechte für alle Server.\n" -" " - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Empty values for variables: %(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Missing variables: %(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "...save record to see the final expression or click the line to edit" -msgstr "" -"... speichere um das Ergebnis zu sehen oder klicke in die Zeile zum Editieren" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-days -msgid "1 day" -msgstr "1 Tag" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-hours -msgid "1 hour" -msgstr "1 Stunde" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-months -msgid "1 month" -msgstr "1 Monat" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-weeks -msgid "1 week" -msgstr "1 Woche" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-years -msgid "1 year" -msgstr "1 Jahr" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__10-minutes -msgid "10 min" -msgstr "10 Minuten" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__12-hours -msgid "12 hour" -msgstr "12 Stunden" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__2-hours -msgid "2 hour" -msgstr "2 Stunden" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__30-minutes -msgid "30 min" -msgstr "30 Minuten" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__6-hours -msgid "6 hour" -msgstr "6 Stunden" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "AND" -msgstr "UND" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"\n" -" x = 2*10\n" -" COMMAND_RESULT = {\"exit_code\": x, \"message\": \"This will be\n" -" logged as an error message because exit code !=0\"}\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"UserError: Warning Exception to use with \n" -" raise" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"env: Odoo Environment on which the action is\n" -" triggered" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"hashlib: Python 'hashlib' library.\n" -" Available methods: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3_224', 'sha3_256',
\n" -" 'sha3_384', 'sha3_512', 'shake_128', 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"hmac: Python 'hmac' library. Use 'new' to create HMAC objects.
\n" -" Available methods on the HMAC *object*: 'update', 'copy', 'digest', 'hexdigest'.
\n" -" Module-level function: 'compare_digest'." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "json: Python 'json' library. Available methods: 'dumps'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"requests: Python 'requests' library. Available methods: 'post'," -" 'get', 'delete', 'request'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "server: Server on which the command is run" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"time, datetime, dateutil\n" -" , timezone: useful Python libraries" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "tower: 'cetmix.tower' helper class shortcut" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "user: Current Odoo user" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "" -"\n" -" &nbsp;" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_log.py:0 -#, python-format -msgid "" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "IPv4 Address:" -msgstr "IPv4 Addresse:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "IPv6 Address:" -msgstr "IPv6 Addresse:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "Operating System:" -msgstr "Betriebssystem:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Partner:" -msgstr "Partner:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -msgid "Servers:" -msgstr "Server:" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_action -msgid "" -"A variable value cannot be assigned multiple times to the same plan line " -"action!" -msgstr "Ein Wert kann nicht mehrmals in der gleichen Planaktion genutzt werden!" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_template -msgid "" -"A variable value cannot be assigned multiple times to the same server " -"template!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_server -msgid "A variable value cannot be assigned multiple times to the same server!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Access" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__access_level -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower_server -msgid "Access Level" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level_warn_msg -msgid "Access Level Warn Msg" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "Access level is not defined for '%(option)s'" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Access level is not defined for '%(variable)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__action -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction -msgid "Action Needed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "" -"Actions trigger based on command result. If empty next command will be " -"executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__active -msgid "Active" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_ids -msgid "Activities" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "Activity State" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Activity Type Icon" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_action -msgid "Add a new file" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_template_action -msgid "Add a new file template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Allow Parallel Run" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "An error occurred: %(error)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Another instance of the command is already running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__applicability -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__applicability -msgid "Applicability" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Archived" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Are you sure?" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_attachment_count -msgid "Attachment Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync -msgid "Auto Sync" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync_interval -msgid "Auto Sync Interval" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Binary" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_view_form -msgid "" -"Can contain English letters, digits and '_'. Leave blank to autogenerate" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Cancel" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for '%(var)s' with value '%(val)s'.\n" -"Try to assigns it to a record instead." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for 'meme' with value 'Pepe'.\n" -"Try to assigns it to a record instead." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Cannot download %(f)s from server: Binary content is not supported for " -"'Text' file type" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot execute command\n" -". CODE: %(status)s. RESULT: %(res)s. ERROR: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Cannot pull %(f)s from server: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot remove test file using command.\n" -" CODE: %(status)s. ERROR: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower -#: model:ir.ui.menu,name:cetmix_tower_server.menu_root -msgid "Cetmix Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command -msgid "Cetmix Tower Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_log -msgid "Cetmix Tower Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_command_execute_wizard_action -msgid "Cetmix Tower Execute Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_auto_pull_files_from_server_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_auto_pull_files_from_server -#: model:ir.cron,name:cetmix_tower_server.ir_cron_auto_pull_files_from_server -msgid "Cetmix Tower File Management: Auto pull files from server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan -msgid "Cetmix Tower Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line -msgid "Cetmix Tower Flight Plan Line" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line_action -msgid "Cetmix Tower Flight Plan Line Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_log -msgid "Cetmix Tower Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_os -msgid "Cetmix Tower Operating System" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_plan_execute_wizard_action -msgid "Cetmix Tower Run Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server -msgid "Cetmix Tower Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_log -msgid "Cetmix Tower Server Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template -msgid "Cetmix Tower Server Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_tag -msgid "Cetmix Tower Tag" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable -msgid "Cetmix Tower Variable" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_option -msgid "Cetmix Tower Variable Options" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_value -msgid "Cetmix Tower Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_mixin -msgid "Cetmix Tower access mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_role_mixin -msgid "Cetmix Tower access role mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key -msgid "Cetmix Tower private key storage" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_reference_mixin -msgid "Cetmix Tower reference mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_template_mixin -msgid "Cetmix Tower template rendering mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Child plans" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Code On Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__color -msgid "Color" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__command_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Command '%(cmd)s' is not compatible with the server '%(server)s'." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__code -msgid "Command Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__command_domain -msgid "Command Domain" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command_log -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__command_log_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__command_log_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_log -#, python-format -msgid "Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Command Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__is_running -msgid "Command is being executed right now" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__command_id -msgid "" -"Command that will be executed to get the log data.\n" -"Be careful with commands that don't support parallel execution!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__line_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_line_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_root -msgid "Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__condition -msgid "Condition" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__condition -msgid "" -"Conditions under which this Flight Plan Line will be launched. e.g.: {{ " -"odoo_version}} == '16.0'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Configuration" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__line_ids -msgid "Configuration Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Confirm" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection failed." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection successful." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Connection test passed! \n" -"%(res)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#, python-format -msgid "Create Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard -msgid "Create new server from template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard_line -msgid "Create new server from template variables" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_uid -msgid "Created by" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_date -msgid "Created on" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Custom Exit Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Custom label. Can be used for search/tracking" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file -msgid "Cx Tower File" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file_template -msgid "Cx Tower File Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Date and time of the latest successful synchronisation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Date and time of the next synchronisation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__path -msgid "Default Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "Default full file name with file type for example: test.txt" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_delete_action -msgid "Delete from server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_dir -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__server_dir -msgid "Directory on server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server__display_name -msgid "Display Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_download_action -msgid "Download" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Due to security restrictions you are not allowed to delete %(fp)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Duration" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "Duration, sec" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Each python code command returns the COMMAND_RESULT value\n" -" which is a dictionary." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_dir -msgid "Eg '/home/user' or '/var/log'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Enter Python code here. Help about Python expression is available in the " -"help tab of this document." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_error -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Error" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Error loading a private key. Unsupported key format or incorrect key." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Execute Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_execute_wizard -msgid "Execute Command in Wizard" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_execute_wizard -msgid "Execute Flight Plan in Wizard" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Execute Result" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute flight plan error" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute flight plan error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute python code error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Execution Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_status -msgid "Exit Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__e -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__e -msgid "Exit with command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__ec -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__ec -msgid "Exit with custom exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Failed" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect after %(attempts)s attempts. Error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect. Error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Failure" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_id -msgid "File" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"File %(f)s is not 'tower' type. This operation is supported for 'tower' " -"files only" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"File %(f)s shouldn't have the '%(src)s' source for the '%(act)s' action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "File Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "File Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "File Type" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File already exists" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file_template.py:0 -#, python-format -msgid "File already exists on server." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__code -msgid "File content" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "File content with variables rendered" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File created and uploaded successfully" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File deleted!" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File downloaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "File from template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "File name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__name -msgid "File name WITHOUT path. Eg 'test.txt'" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File source cannot be determined: '%(source)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_id -msgid "File that will be executed to get the log data" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File uploaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "File will be disconnected from template. Continue?" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "File will be kept on server when deleted in Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_count -msgid "File(s)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Files" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files deleted!" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files downloaded!" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files uploaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Finish date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__finish_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__finish_date -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Finished" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_line_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Flight Plan Lines" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan_log -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan_log -msgid "Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Flight Plan Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Flight Plan line that is being currently executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan -msgid "Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "Flight plan '%(plan)s' is not compatible with the server '%(server)s'." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_follower_ids -msgid "Followers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_channel_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_channel_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_channel_ids -msgid "Followers (Channels)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_partner_ids -msgid "Followers (Partners)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Font awesome icon e.g. fa-tasks" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__color -msgid "For better visualization in views" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "For how long a flight plan is already running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__applicability__this -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_execute_wizard__applicability__this -msgid "For selected server(s)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__full_server_path -msgid "Full Server Path" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "General Settings" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__is_global -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Global" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Group By" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__has_missing_required_values -msgid "Has Missing Required Values" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Has Template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Help" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Help with Python expressions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server__id -msgid "ID" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v4_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v4_address -msgid "IPv4 Address" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v6_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v6_address -msgid "IPv6 Address" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_unread -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_unread -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_unread -msgid "If checked, new messages require your attention." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "If checked, some messages have a delivery error." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -msgid "" -"If enabled command can be run on the same server while the same command is still running.\n" -"Returns ANOTHER_COMMAND_RUNNING if execution is blocked" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__auto_sync -msgid "If enabled file will be synced automatically using cron" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -msgid "" -"If enabled flightplan can be run on the same server while the same flightplan is still running.\n" -"Returns -5 status is execution is blocked" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#, python-format -msgid "If exit code" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_plan.py:0 -#, python-format -msgid "If exit code == 35 then Exit with command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -msgid "Indicates if this variable is mandatory for server creation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_is_follower -msgid "Is Follower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_running -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__is_running -msgid "Is Running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_skipped -msgid "Is Skipped" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "Keep When Deleted" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__k -msgid "Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__key_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Key Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Key reference for inline usage" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_key -msgid "Keys and Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Label" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Labeled" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server____last_update -msgid "Last Modified on" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Last Sync Date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_uid -msgid "Last Updated by" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_date -msgid "Last Updated on" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Latest version of file content on server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__partner_id -msgid "Leave blank to use for any partner" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Leave blank to use with any partner" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Leave blank to use with any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__line_id -msgid "Line" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Local" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "" -"Location where command will be executed. Overrides command default path. You" -" can use {{ variables }} in path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__path -msgid "" -"Location where command will be executed. You can use {{ variables }} in path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_text -msgid "Log Text" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_type -msgid "Log Type" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_main_attachment_id -msgid "Main Attachment" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__parent_flight_plan_log_id -msgid "Main Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plans" -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_manager -msgid "Manager" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers who can modify this record" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "Message Delivery error" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_ids -msgid "Messages" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables -msgid "Missing Required Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables_message -msgid "Missing Required Variables Message" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key_mixin -msgid "Mixin for managing secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Modify Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__name -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_summary -msgid "Next Activity Summary" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_id -msgid "Next Activity Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Next Sync Date" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Synced" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Template" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"No output received. Please log in manually and check for any issues.\n" -"===\n" -"CODE: %(status)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "No runner found for command action '%(cmd_action)s'" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "No server found for the provided reference." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "No sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__applicability__shared -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_execute_wizard__applicability__shared -msgid "Non server restricted" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Not Running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__note -msgid "Note" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of errors" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of messages which requires an action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of messages with delivery error" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_unread_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_unread_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_unread_counter -msgid "Number of unread messages" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_os -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "OS" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__os_ids -msgid "OSes" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_os -msgid "OSs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_delete_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "On Delete Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "On Error" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Only one global value can be defined for variable '%(var)s'" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "Only one global value can be defined for variable 'meme'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open full form" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__os_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__os_id -msgid "Operating System" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__option_id -msgid "Option" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Option '%(val)s' is not available for variable '%(var)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__option_ids -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__o -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Options" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__partner_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__partner_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Partner" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__p -msgid "Password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__path -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_domain -msgid "Plan Domain" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__plan_line_action_id -msgid "Plan Line Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Plan Line Executed" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_plan_execute_wizard.py:0 -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__plan_log_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_log_ids -#, python-format -msgid "Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__is_running -msgid "Plan is being executed right now" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Plan line condition check failed." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide IPv4 or IPv6 address for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH Key for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH password for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py:0 -#, python-format -msgid "" -"Please provide values for the following configuration variables: " -"%(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "Please resolve the following issues with configuration variables:" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Please select a command to execute" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Post Run Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Preview" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__parent_id -msgid "Previous Version" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Pull from Server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Push to Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__path -msgid "" -"Put custom path to run the command.\n" -"IMPORTANT: this field does NOT support variables!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Put your notes here" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Put your notes here..." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__action__python_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Python code" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Recursive plan call detected in plan %(name)s." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Reference" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Reference Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_command_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_file_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_project_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_remote_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_source_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_key_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_os_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_action_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_reference_mixin_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_log_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_tag_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_reference_unique -msgid "Reference must be unique" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_key.py:0 -#, python-format -msgid "Reference must be unique for the combination of partner and server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "" -"Reference. Can contain English letters, digits and '_'. Leave blank to " -"autogenerate" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -msgid "Refresh" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Refresh All" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote\n" -" one." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote " -"one." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__rendered_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "Rendered Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_name -msgid "Rendered Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_server_dir -msgid "Rendered Server Dir" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__required -msgid "Required" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_response -msgid "Response" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_user_id -msgid "Responsible User" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__result -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Result" -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_root -msgid "Root" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"Run\n" -" Command" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"Run\n" -" Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#, python-format -msgid "Run Command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_plan -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#, python-format -msgid "Run Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run New Command" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Run a flight plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "" -"Run code as it appears in 'Rendered code' in wizard and return to wizard. " -"Result will not be logged" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run code using sever method and log result" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Run command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__use_sudo -msgid "Run commands using 'sudo'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run in wizard" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__n -msgid "Run next command" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run on" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Running" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Running Now" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_auth_mode -msgid "SSH Auth Mode" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH Client is not defined." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "SSH Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__k -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "SSH Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_key -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "SSH Key / Secret" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_password -msgid "SSH Password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__secret_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_key_id -msgid "SSH Private Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "SSH Username" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__action__ssh_command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "SSH command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH connection error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH execute command error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_port -msgid "SSH port" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Search Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Search Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -msgid "Search File Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Search Files" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Search Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Search Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Search Keys/Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_search_view -msgid "Search OS" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Search Server Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Search Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_search_view -msgid "Search Tags" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Search Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__s -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Secret" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__secret_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__applicability -msgid "" -"Selected server(s): only Commands that are specific to the selected server(s)\n" -"Non server restricted: all Commands that are not specific to any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_execute_wizard__applicability -msgid "" -"Selected server(s): only Flight Plans that are specific to the selected server(s)\n" -"Non server restricted: all Flight Plans that are not specific to any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__sequence -msgid "Sequence" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_ir_actions_server -msgid "Server Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_count -msgid "Server Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_log_ids -msgid "Server Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_log_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__name -msgid "Server Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_response -msgid "Server Response" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_status -msgid "Server Status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_template_id -msgid "Server Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server_template -msgid "Server Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server URL, eg 'https://meme.example.com'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Server Version" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Server not found" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_response -msgid "" -"Server response received during the last operation.\n" -"Default value if no error happened is 'ok'.\n" -"Otherwise there will be a server error message logged." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Server tight" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__url -msgid "Server web interface, eg 'https://doge.example.com'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__server_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_ids -msgid "" -"Servers on which the command will be executed.\n" -"If empty, command canbe executed on all servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Set Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_status -msgid "" -"Set the following status if command is executed successfully. Leave " -"'Undefined' if you don't need to update the status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_settings -msgid "Settings" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Show Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Show Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__show_servers -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__show_servers -msgid "Show Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Skipped" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Some servers don't support this command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Some variable options are invalid:\n" -"%(detailed_message)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__source -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__source -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Source" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Start date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__start_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__start_date -msgid "Started" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_status -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__status -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "" -"Status based on activities\n" -"Overdue: Due date is already passed\n" -"Today: Activity date is today\n" -"Planned: Future activities." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__s -msgid "String" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#, python-format -msgid "Success" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__use_sudo__p -msgid "Sudo with password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__use_sudo__n -msgid "Sudo without password" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Sync Error" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Synced" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_tag -msgid "Tag" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Tagged" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__tag_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_tag -msgid "Tags" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Template Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_template_action -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_template -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_template -msgid "Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Test Connection" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Text" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "" -"The access level for Variable Option '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Option Access Level: %(val_level)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"The access level for Variable Value '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Value Access Level: %(val_level)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "" -"The access level of command(s) '%(command_names)s' included in the current " -"Flight plan is higher than the access level of the Flight plan itself. " -"Please ensure that you want to allow those commands to be run anyway." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_unique_variable_option -msgid "The combination of Name,Value and Variable must be unique." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "The file %(f_path)s not found." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Then" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "There are two default keys in the dictionary, e.g.:" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__plan_delete_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "This Flightplan will be executed when the server is deleted" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "" -"This action will be executed on error if no command action can be applied" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "This command can be used only in Flight Plans." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__note -msgid "This field is used to put some notes regarding template." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_template_mixin__code -msgid "This field will be rendered by default" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_template_id -msgid "" -"This file template will be used to create log files when server is created " -"from a template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -msgid "This flight plan will be run upon server creation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "" -"This is required, however you can change this later in the server settings" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -msgid "This template will be used to create or update the pushed file" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Time consumed for execution, seconds" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_tools -msgid "Tools" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_count -msgid "Total Files" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__tower -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__tower -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_mixin -msgid "Tower Variables mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cetmix_tower -msgid "Tower automation helper model" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__triggered_plan_log_id -msgid "Triggered Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_type -msgid "Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Type of the exception activity on record." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__url -msgid "URL" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to delete file '%(f)s'.\n" -"Delete operation is not supported for 'server' type files." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to upload file '%(f)s'.\n" -"Upload operation is not supported for 'server' type files." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_unread -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_unread -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_unread -msgid "Unread Messages" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_unread_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_unread_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_unread_counter -msgid "Unread Messages Counter" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_upload_action -msgid "Upload" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__use_sudo -msgid "Use Sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__use_sudo -msgid "Use sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH key in the following servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Used for" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__server_id -msgid "Used for selected server only. Leave blank to use globally" -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_user -msgid "User" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users who can view this record" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Value" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids_count -msgid "Value Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Variable" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Variable '%(var)s' can only be assigned to one of the models at a time: " -"Server, Server Template, or Plan Line Action." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -msgid "Variable Reference" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable_value -#, python-format -msgid "Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_tower_variable_value_uniq -msgid "Variable can be declared only once for the same record!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_name_uniq -msgid "Variable names must be unique" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable not found" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Variable reference '%(var_ref)s' has an invalid option reference " -"'%(opt_ref)s'." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value created" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value updated" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -msgid "Variable values for selected record" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable -msgid "Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Various fields may use Python code or Python expressions. The\n" -" following variables can be used:" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Where command was executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Will be used instead of the command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__use_sudo -msgid "" -"Will use sudo based on server settings.If no sudo is configured will run " -"without sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__p -msgid "With password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__n -msgid "Without password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__wizard_id -msgid "Wizard" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "Wrong action" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You are not allowed to execute commands in wizard" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You cannot execute an empty command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You cannot run custom code on multiple servers at once." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/ir_actions_server.py:0 -#, python-format -msgid "" -"You need to have 'write' access to all servers you want to run this action " -"on." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "e.g. /home/user This field does NOT support variables" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "e.g. /such/much/{{ path }}, overrides command path" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/common.py:0 -#: code:addons/cetmix_tower_server/tests/common.py:0 -#, python-format -msgid "groups_ref must be string or list of strings!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "managers who can modify this record" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "managers who can modify this server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "managers who can modify this template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "new server name" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "optional, eg /home/{{ tower.server.username }}" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "sudo password was not provided!" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "then" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "this can be changed later" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "users who can access this server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "users who can access this template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "users who can view this record" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "with tags" -msgstr "" diff --git a/addons/cetmix_tower_server/i18n/fi.po b/addons/cetmix_tower_server/i18n/fi.po deleted file mode 100644 index 1bd0423..0000000 --- a/addons/cetmix_tower_server/i18n/fi.po +++ /dev/null @@ -1,3449 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * cetmix_tower_server -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" -"Language: fi\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__source -msgid "" -"\n" -" - Tower: file is pushed from Tower to server.\n" -" - Server: file is pulled from server to Tower.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_user -msgid "" -"\n" -" Basic actions for selected servers.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_manager -msgid "" -"\n" -" Create and modify selected servers.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_root -msgid "" -"\n" -" Full control over all servers.\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Empty values for variables: %(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Missing variables: %(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "...save record to see the final expression or click the line to edit" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-days -msgid "1 day" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-hours -msgid "1 hour" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-months -msgid "1 month" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-weeks -msgid "1 week" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-years -msgid "1 year" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__10-minutes -msgid "10 min" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__12-hours -msgid "12 hour" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__2-hours -msgid "2 hour" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__30-minutes -msgid "30 min" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__6-hours -msgid "6 hour" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "AND" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"\n" -" x = 2*10\n" -" COMMAND_RESULT = {\"exit_code\": x, \"message\": \"This will be\n" -" logged as an error message because exit code !=0\"}\n" -" " -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"UserError: Warning Exception to use with \n" -" raise" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"env: Odoo Environment on which the action is\n" -" triggered" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"hashlib: Python 'hashlib' library.\n" -" Available methods: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3_224', 'sha3_256',
\n" -" 'sha3_384', 'sha3_512', 'shake_128', 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"hmac: Python 'hmac' library. Use 'new' to create HMAC objects.
\n" -" Available methods on the HMAC *object*: 'update', 'copy', 'digest', 'hexdigest'.
\n" -" Module-level function: 'compare_digest'." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "json: Python 'json' library. Available methods: 'dumps'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"requests: Python 'requests' library. Available methods: 'post'," -" 'get', 'delete', 'request'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "server: Server on which the command is run" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"time, datetime, dateutil\n" -" , timezone: useful Python libraries" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "tower: 'cetmix.tower' helper class shortcut" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "user: Current Odoo user" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "" -"\n" -" &nbsp;" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_log.py:0 -#, python-format -msgid "" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "IPv4 Address:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "IPv6 Address:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "Operating System:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Partner:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -msgid "Servers:" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_action -msgid "" -"A variable value cannot be assigned multiple times to the same plan line " -"action!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_template -msgid "" -"A variable value cannot be assigned multiple times to the same server " -"template!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_server -msgid "A variable value cannot be assigned multiple times to the same server!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Access" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__access_level -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower_server -msgid "Access Level" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level_warn_msg -msgid "Access Level Warn Msg" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "Access level is not defined for '%(option)s'" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Access level is not defined for '%(variable)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__action -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction -msgid "Action Needed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "" -"Actions trigger based on command result. If empty next command will be " -"executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__active -msgid "Active" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_ids -msgid "Activities" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "Activity State" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Activity Type Icon" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_action -msgid "Add a new file" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_template_action -msgid "Add a new file template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Allow Parallel Run" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "An error occurred: %(error)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Another instance of the command is already running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__applicability -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__applicability -msgid "Applicability" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Archived" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Are you sure?" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_attachment_count -msgid "Attachment Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync -msgid "Auto Sync" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync_interval -msgid "Auto Sync Interval" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Binary" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_view_form -msgid "" -"Can contain English letters, digits and '_'. Leave blank to autogenerate" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Cancel" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for '%(var)s' with value '%(val)s'.\n" -"Try to assigns it to a record instead." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for 'meme' with value 'Pepe'.\n" -"Try to assigns it to a record instead." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Cannot download %(f)s from server: Binary content is not supported for " -"'Text' file type" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot execute command\n" -". CODE: %(status)s. RESULT: %(res)s. ERROR: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Cannot pull %(f)s from server: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot remove test file using command.\n" -" CODE: %(status)s. ERROR: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower -#: model:ir.ui.menu,name:cetmix_tower_server.menu_root -msgid "Cetmix Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command -msgid "Cetmix Tower Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_log -msgid "Cetmix Tower Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_command_execute_wizard_action -msgid "Cetmix Tower Execute Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_auto_pull_files_from_server_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_auto_pull_files_from_server -#: model:ir.cron,name:cetmix_tower_server.ir_cron_auto_pull_files_from_server -msgid "Cetmix Tower File Management: Auto pull files from server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan -msgid "Cetmix Tower Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line -msgid "Cetmix Tower Flight Plan Line" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line_action -msgid "Cetmix Tower Flight Plan Line Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_log -msgid "Cetmix Tower Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_os -msgid "Cetmix Tower Operating System" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_plan_execute_wizard_action -msgid "Cetmix Tower Run Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server -msgid "Cetmix Tower Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_log -msgid "Cetmix Tower Server Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template -msgid "Cetmix Tower Server Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_tag -msgid "Cetmix Tower Tag" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable -msgid "Cetmix Tower Variable" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_option -msgid "Cetmix Tower Variable Options" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_value -msgid "Cetmix Tower Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_mixin -msgid "Cetmix Tower access mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_role_mixin -msgid "Cetmix Tower access role mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key -msgid "Cetmix Tower private key storage" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_reference_mixin -msgid "Cetmix Tower reference mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_template_mixin -msgid "Cetmix Tower template rendering mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Child plans" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Code On Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__color -msgid "Color" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__command_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Command '%(cmd)s' is not compatible with the server '%(server)s'." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__code -msgid "Command Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__command_domain -msgid "Command Domain" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command_log -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__command_log_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__command_log_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_log -#, python-format -msgid "Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Command Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__is_running -msgid "Command is being executed right now" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__command_id -msgid "" -"Command that will be executed to get the log data.\n" -"Be careful with commands that don't support parallel execution!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__line_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_line_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_root -msgid "Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__condition -msgid "Condition" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__condition -msgid "" -"Conditions under which this Flight Plan Line will be launched. e.g.: {{ " -"odoo_version}} == '16.0'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Configuration" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__line_ids -msgid "Configuration Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Confirm" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection failed." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection successful." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Connection test passed! \n" -"%(res)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#, python-format -msgid "Create Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard -msgid "Create new server from template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard_line -msgid "Create new server from template variables" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_uid -msgid "Created by" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_date -msgid "Created on" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Custom Exit Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Custom label. Can be used for search/tracking" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file -msgid "Cx Tower File" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file_template -msgid "Cx Tower File Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Date and time of the latest successful synchronisation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Date and time of the next synchronisation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__path -msgid "Default Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "Default full file name with file type for example: test.txt" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_delete_action -msgid "Delete from server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_dir -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__server_dir -msgid "Directory on server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server__display_name -msgid "Display Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_download_action -msgid "Download" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Due to security restrictions you are not allowed to delete %(fp)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Duration" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "Duration, sec" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Each python code command returns the COMMAND_RESULT value\n" -" which is a dictionary." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_dir -msgid "Eg '/home/user' or '/var/log'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Enter Python code here. Help about Python expression is available in the " -"help tab of this document." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_error -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Error" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Error loading a private key. Unsupported key format or incorrect key." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Execute Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_execute_wizard -msgid "Execute Command in Wizard" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_execute_wizard -msgid "Execute Flight Plan in Wizard" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Execute Result" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute flight plan error" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute flight plan error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute python code error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Execution Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_status -msgid "Exit Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__e -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__e -msgid "Exit with command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__ec -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__ec -msgid "Exit with custom exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Failed" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect after %(attempts)s attempts. Error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect. Error: %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Failure" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_id -msgid "File" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"File %(f)s is not 'tower' type. This operation is supported for 'tower' " -"files only" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"File %(f)s shouldn't have the '%(src)s' source for the '%(act)s' action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "File Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "File Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "File Type" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File already exists" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file_template.py:0 -#, python-format -msgid "File already exists on server." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__code -msgid "File content" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "File content with variables rendered" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File created and uploaded successfully" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File deleted!" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File downloaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "File from template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "File name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__name -msgid "File name WITHOUT path. Eg 'test.txt'" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File source cannot be determined: '%(source)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_id -msgid "File that will be executed to get the log data" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File uploaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "File will be disconnected from template. Continue?" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "File will be kept on server when deleted in Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_count -msgid "File(s)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Files" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files deleted!" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files downloaded!" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files uploaded!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Finish date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__finish_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__finish_date -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Finished" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_line_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Flight Plan Lines" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan_log -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan_log -msgid "Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Flight Plan Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Flight Plan line that is being currently executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan -msgid "Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "Flight plan '%(plan)s' is not compatible with the server '%(server)s'." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_follower_ids -msgid "Followers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_channel_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_channel_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_channel_ids -msgid "Followers (Channels)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_partner_ids -msgid "Followers (Partners)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Font awesome icon e.g. fa-tasks" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__color -msgid "For better visualization in views" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "For how long a flight plan is already running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__applicability__this -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_execute_wizard__applicability__this -msgid "For selected server(s)" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__full_server_path -msgid "Full Server Path" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "General Settings" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__is_global -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Global" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Group By" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__has_missing_required_values -msgid "Has Missing Required Values" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Has Template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Help" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Help with Python expressions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server__id -msgid "ID" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v4_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v4_address -msgid "IPv4 Address" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v6_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v6_address -msgid "IPv6 Address" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_unread -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_unread -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_unread -msgid "If checked, new messages require your attention." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "If checked, some messages have a delivery error." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -msgid "" -"If enabled command can be run on the same server while the same command is still running.\n" -"Returns ANOTHER_COMMAND_RUNNING if execution is blocked" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__auto_sync -msgid "If enabled file will be synced automatically using cron" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -msgid "" -"If enabled flightplan can be run on the same server while the same flightplan is still running.\n" -"Returns -5 status is execution is blocked" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#, python-format -msgid "If exit code" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_plan.py:0 -#, python-format -msgid "If exit code == 35 then Exit with command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -msgid "Indicates if this variable is mandatory for server creation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_is_follower -msgid "Is Follower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_running -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__is_running -msgid "Is Running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_skipped -msgid "Is Skipped" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "Keep When Deleted" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__k -msgid "Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__key_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Key Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Key reference for inline usage" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_key -msgid "Keys and Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Label" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Labeled" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server____last_update -msgid "Last Modified on" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Last Sync Date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_uid -msgid "Last Updated by" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_date -msgid "Last Updated on" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Latest version of file content on server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__partner_id -msgid "Leave blank to use for any partner" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Leave blank to use with any partner" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Leave blank to use with any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__line_id -msgid "Line" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Local" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "" -"Location where command will be executed. Overrides command default path. You" -" can use {{ variables }} in path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__path -msgid "" -"Location where command will be executed. You can use {{ variables }} in path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_text -msgid "Log Text" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_type -msgid "Log Type" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_main_attachment_id -msgid "Main Attachment" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__parent_flight_plan_log_id -msgid "Main Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plans" -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_manager -msgid "Manager" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers who can modify this record" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "Message Delivery error" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_ids -msgid "Messages" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables -msgid "Missing Required Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables_message -msgid "Missing Required Variables Message" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key_mixin -msgid "Mixin for managing secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Modify Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__name -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_summary -msgid "Next Activity Summary" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_id -msgid "Next Activity Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Next Sync Date" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Synced" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Template" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"No output received. Please log in manually and check for any issues.\n" -"===\n" -"CODE: %(status)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "No runner found for command action '%(cmd_action)s'" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "No server found for the provided reference." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "No sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__applicability__shared -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_execute_wizard__applicability__shared -msgid "Non server restricted" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Not Running" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__note -msgid "Note" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of errors" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of messages which requires an action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of messages with delivery error" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_unread_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_unread_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_unread_counter -msgid "Number of unread messages" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_os -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "OS" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__os_ids -msgid "OSes" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_os -msgid "OSs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_delete_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "On Delete Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "On Error" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Only one global value can be defined for variable '%(var)s'" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "Only one global value can be defined for variable 'meme'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open full form" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__os_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__os_id -msgid "Operating System" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__option_id -msgid "Option" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Option '%(val)s' is not available for variable '%(var)s'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__option_ids -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__o -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Options" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__partner_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__partner_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Partner" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__p -msgid "Password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__path -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "Path" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_domain -msgid "Plan Domain" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__plan_line_action_id -msgid "Plan Line Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Plan Line Executed" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_plan_execute_wizard.py:0 -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__plan_log_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_log_ids -#, python-format -msgid "Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__is_running -msgid "Plan is being executed right now" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Plan line condition check failed." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide IPv4 or IPv6 address for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH Key for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH password for %(srv)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py:0 -#, python-format -msgid "" -"Please provide values for the following configuration variables: " -"%(variables)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "Please resolve the following issues with configuration variables:" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Please select a command to execute" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Post Run Actions" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Preview" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__parent_id -msgid "Previous Version" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Pull from Server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Push to Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__path -msgid "" -"Put custom path to run the command.\n" -"IMPORTANT: this field does NOT support variables!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Put your notes here" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Put your notes here..." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__action__python_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Python code" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Recursive plan call detected in plan %(name)s." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Reference" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Reference Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_command_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_file_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_project_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_remote_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_source_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_key_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_os_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_action_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_reference_mixin_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_log_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_tag_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_reference_unique -msgid "Reference must be unique" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_key.py:0 -#, python-format -msgid "Reference must be unique for the combination of partner and server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "" -"Reference. Can contain English letters, digits and '_'. Leave blank to " -"autogenerate" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -msgid "Refresh" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Refresh All" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote\n" -" one." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote " -"one." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__rendered_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "Rendered Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_name -msgid "Rendered Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_server_dir -msgid "Rendered Server Dir" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__required -msgid "Required" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_response -msgid "Response" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_user_id -msgid "Responsible User" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__result -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Result" -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_root -msgid "Root" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"Run\n" -" Command" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"Run\n" -" Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#, python-format -msgid "Run Command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_plan -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#, python-format -msgid "Run Flight Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run New Command" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run Plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Run a flight plan" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "" -"Run code as it appears in 'Rendered code' in wizard and return to wizard. " -"Result will not be logged" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run code using sever method and log result" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Run command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__use_sudo -msgid "Run commands using 'sudo'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run in wizard" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__n -msgid "Run next command" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run on" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Running" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Running Now" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_auth_mode -msgid "SSH Auth Mode" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH Client is not defined." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "SSH Command" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__k -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "SSH Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_key -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "SSH Key / Secret" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_password -msgid "SSH Password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__secret_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_key_id -msgid "SSH Private Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "SSH Username" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__action__ssh_command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "SSH command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH connection error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH execute command error %(err)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_port -msgid "SSH port" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Search Command Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Search Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -msgid "Search File Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Search Files" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Search Flight Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Search Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Search Keys/Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_search_view -msgid "Search OS" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Search Server Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Search Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_search_view -msgid "Search Tags" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Search Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__s -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Secret" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__secret_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__applicability -msgid "" -"Selected server(s): only Commands that are specific to the selected server(s)\n" -"Non server restricted: all Commands that are not specific to any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_execute_wizard__applicability -msgid "" -"Selected server(s): only Flight Plans that are specific to the selected server(s)\n" -"Non server restricted: all Flight Plans that are not specific to any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__sequence -msgid "Sequence" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_ir_actions_server -msgid "Server Action" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_count -msgid "Server Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_log_ids -msgid "Server Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_log_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server Logs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__name -msgid "Server Name" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_response -msgid "Server Response" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_status -msgid "Server Status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_template_id -msgid "Server Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server_template -msgid "Server Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server URL, eg 'https://meme.example.com'" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Server Version" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Server not found" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_response -msgid "" -"Server response received during the last operation.\n" -"Default value if no error happened is 'ok'.\n" -"Otherwise there will be a server error message logged." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Server tight" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__url -msgid "Server web interface, eg 'https://doge.example.com'" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__server_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_ids -msgid "" -"Servers on which the command will be executed.\n" -"If empty, command canbe executed on all servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Set Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_status -msgid "" -"Set the following status if command is executed successfully. Leave " -"'Undefined' if you don't need to update the status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_settings -msgid "Settings" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Show Commands" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Show Flight Plans" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__show_servers -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__show_servers -msgid "Show Servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Skipped" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Some servers don't support this command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Some variable options are invalid:\n" -"%(detailed_message)s" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__source -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__source -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Source" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Start date" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__start_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__start_date -msgid "Started" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_status -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__status -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "" -"Status based on activities\n" -"Overdue: Due date is already passed\n" -"Today: Activity date is today\n" -"Planned: Future activities." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__s -msgid "String" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#, python-format -msgid "Success" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__use_sudo__p -msgid "Sudo with password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__use_sudo__n -msgid "Sudo without password" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Sync Error" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Synced" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_tag -msgid "Tag" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Tagged" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__tag_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_tag -msgid "Tags" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Template Code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_template_action -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_template -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_template -msgid "Templates" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Test Connection" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Text" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "" -"The access level for Variable Option '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Option Access Level: %(val_level)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"The access level for Variable Value '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Value Access Level: %(val_level)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "" -"The access level of command(s) '%(command_names)s' included in the current " -"Flight plan is higher than the access level of the Flight plan itself. " -"Please ensure that you want to allow those commands to be run anyway." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_unique_variable_option -msgid "The combination of Name,Value and Variable must be unique." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "The file %(f_path)s not found." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Then" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "There are two default keys in the dictionary, e.g.:" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__plan_delete_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "This Flightplan will be executed when the server is deleted" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "" -"This action will be executed on error if no command action can be applied" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "This command can be used only in Flight Plans." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__note -msgid "This field is used to put some notes regarding template." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_template_mixin__code -msgid "This field will be rendered by default" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_template_id -msgid "" -"This file template will be used to create log files when server is created " -"from a template" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -msgid "This flight plan will be run upon server creation" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "" -"This is required, however you can change this later in the server settings" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -msgid "This template will be used to create or update the pushed file" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Time consumed for execution, seconds" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_tools -msgid "Tools" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_count -msgid "Total Files" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__tower -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__tower -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_mixin -msgid "Tower Variables mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cetmix_tower -msgid "Tower automation helper model" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__triggered_plan_log_id -msgid "Triggered Plan Log" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_type -msgid "Type" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Type of the exception activity on record." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__url -msgid "URL" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to delete file '%(f)s'.\n" -"Delete operation is not supported for 'server' type files." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to upload file '%(f)s'.\n" -"Upload operation is not supported for 'server' type files." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_unread -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_unread -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_unread -msgid "Unread Messages" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_unread_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_unread_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_unread_counter -msgid "Unread Messages Counter" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_upload_action -msgid "Upload" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__use_sudo -msgid "Use Sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__use_sudo -msgid "Use sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH Key" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH key in the following servers" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Used for" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__server_id -msgid "Used for selected server only. Leave blank to use globally" -msgstr "" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_user -msgid "User" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users who can view this record" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Value" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids_count -msgid "Value Count" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Variable" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Variable '%(var)s' can only be assigned to one of the models at a time: " -"Server, Server Template, or Plan Line Action." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -msgid "Variable Reference" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable_value -#, python-format -msgid "Variable Values" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_tower_variable_value_uniq -msgid "Variable can be declared only once for the same record!" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_name_uniq -msgid "Variable names must be unique" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable not found" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Variable reference '%(var_ref)s' has an invalid option reference " -"'%(opt_ref)s'." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value created" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value updated" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -msgid "Variable values for selected record" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable -msgid "Variables" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Various fields may use Python code or Python expressions. The\n" -" following variables can be used:" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Where command was executed" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Will be used instead of the command exit code" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__use_sudo -msgid "" -"Will use sudo based on server settings.If no sudo is configured will run " -"without sudo" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__p -msgid "With password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__n -msgid "Without password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__wizard_id -msgid "Wizard" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "Wrong action" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You are not allowed to execute commands in wizard" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You cannot execute an empty command" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You cannot run custom code on multiple servers at once." -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/ir_actions_server.py:0 -#, python-format -msgid "" -"You need to have 'write' access to all servers you want to run this action " -"on." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "e.g. /home/user This field does NOT support variables" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "e.g. /such/much/{{ path }}, overrides command path" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/common.py:0 -#: code:addons/cetmix_tower_server/tests/common.py:0 -#, python-format -msgid "groups_ref must be string or list of strings!" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "managers who can modify this record" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "managers who can modify this server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "managers who can modify this template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "new server name" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "optional, eg /home/{{ tower.server.username }}" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "sudo password was not provided!" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "then" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "this can be changed later" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "users who can access this server" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "users who can access this template" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "users who can view this record" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "with tags" -msgstr "" diff --git a/addons/cetmix_tower_server/i18n/hr.po b/addons/cetmix_tower_server/i18n/hr.po deleted file mode 100644 index 0cb0b3c..0000000 --- a/addons/cetmix_tower_server/i18n/hr.po +++ /dev/null @@ -1,3591 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * cetmix_tower_server -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: weblatereports@cetmix.com\n" -"PO-Revision-Date: 2025-03-06 09:11+0000\n" -"Last-Translator: Bole \n" -"Language-Team: Croatian \n" -"Language: hr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Weblate 5.10.3-dev\n" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__source -msgid "" -"\n" -" - Tower: file is pushed from Tower to server.\n" -" - Server: file is pulled from server to Tower.\n" -" " -msgstr "" -"\n" -" - Tower: datoteka se kopira sa Tower na server.\n" -" - Server: datoteka se povlači sa servera na Tower.\n" -" " - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_user -msgid "" -"\n" -" Basic actions for selected servers.\n" -" " -msgstr "" -"\n" -" Osnovne radnje za odabrane servere.\n" -" " - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_manager -msgid "" -"\n" -" Create and modify selected servers.\n" -" " -msgstr "" -"\n" -" Kreiraj i uredi odabrane servere.\n" -" " - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_root -msgid "" -"\n" -" Full control over all servers.\n" -" " -msgstr "" -"\n" -" Puna kontrola nad svim serverima.\n" -" " - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Empty values for variables: %(variables)s" -msgstr " - Isprazni vrijednosti za varijable: %(variables)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Missing variables: %(variables)s" -msgstr " - Nedostaju variable: %(variables)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "...save record to see the final expression or click the line to edit" -msgstr "" -"... spremite zapis da biste vidjeli konačni izraz ili kliknite na liniju za " -"uređivanje" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-days -msgid "1 day" -msgstr "1 dan" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-hours -msgid "1 hour" -msgstr "1 sat" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-months -msgid "1 month" -msgstr "1 mjesec" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-weeks -msgid "1 week" -msgstr "1 tjedan" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__1-years -msgid "1 year" -msgstr "1 godina" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__10-minutes -msgid "10 min" -msgstr "10 min" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__12-hours -msgid "12 hour" -msgstr "12 sati" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__2-hours -msgid "2 hour" -msgstr "2 sata" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__30-minutes -msgid "30 min" -msgstr "30 min" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__auto_sync_interval__6-hours -msgid "6 hour" -msgstr "6 sati" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "AND" -msgstr "I" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"\n" -" x = 2*10\n" -" COMMAND_RESULT = {\"exit_code\": x, \"message\": \"This will be\n" -" logged as an error message because exit code !=0\"}\n" -" " -msgstr "" -"\n" -" x = 2*10\n" -" COMMAND_RESULT = {\"exit_code\": x, " -"\"message\": \"Ovo će biti \n" -" zapisano kao poruka greške jer je kod " -"izlaza !=0\"}\n" -" " - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"UserError: Warning Exception to use with \n" -" raise" -msgstr "" -"UserError: Poruka greške za korištenje sa \n" -" raise" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"env: Odoo Environment on which the action is\n" -" triggered" -msgstr "" -"env: Odoo Environment na kojem se pokreće\n" -" akcija" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"hashlib: Python 'hashlib' library.\n" -" Available methods: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3_224', 'sha3_256',
\n" -" 'sha3_384', 'sha3_512', 'shake_128', 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'" -msgstr "" -"hashlib: Python 'hashlib' library.\n" -" Dostupne metode: 'sha1', 'sha224', " -"'sha256', 'sha384', 'sha512', 'sha3_224', 'sha3_256',
\n" -" 'sha3_384', 'sha3_512', " -"'shake_128', 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"hmac: Python 'hmac' library. Use 'new' to create HMAC objects.
\n" -" Available methods on the HMAC *object*: 'update', 'copy', 'digest', 'hexdigest'.
\n" -" Module-level function: 'compare_digest'." -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "json: Python 'json' library. Available methods: 'dumps'" -msgstr "json: Python 'json' library. Dostupne metode: 'dumps'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"requests: Python 'requests' library. Available methods: 'post'," -" 'get', 'delete', 'request'" -msgstr "" -"requests: Python 'requests' library. Dostupne metode: 'post', " -"'get', 'delete', 'request'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "server: Server on which the command is run" -msgstr "server: Server na kojem se pokreće naredba" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"time, datetime, dateutil\n" -" , timezone: useful Python libraries" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "tower: 'cetmix.tower' helper class shortcut" -msgstr "tower: 'cetmix.tower' prečac do pomoćne klase" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "user: Current Odoo user" -msgstr "user: Trenutni Odoo korisnik" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"" -msgstr "" -"" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "" -"\n" -" &nbsp;" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_log.py:0 -#, python-format -msgid "" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "IPv4 Address:" -msgstr "IPv4 Adresa:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "IPv6 Address:" -msgstr "IPv6 Adresa:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban_manager -msgid "Operating System:" -msgstr "Operativni sistem:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Partner:" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -msgid "Servers:" -msgstr "Serveri:" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_action -msgid "" -"A variable value cannot be assigned multiple times to the same plan line " -"action!" -msgstr "" -"Vrijednost varijable nije moguće dodijeliti više puta na jednu akciju plana!" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_template -msgid "" -"A variable value cannot be assigned multiple times to the same server " -"template!" -msgstr "" -"Vrijednost varijable nije moguće dodijeliti više puta na jedan predložak " -"servera!" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_server -msgid "A variable value cannot be assigned multiple times to the same server!" -msgstr "Vrijednost varijable nije moguće dodijeliti više puta na isti server!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Access" -msgstr "Pristup" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__access_level -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower_server -msgid "Access Level" -msgstr "Razina pristupa" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level_warn_msg -msgid "Access Level Warn Msg" -msgstr "Poruka upozorenja za Razinu pristupa" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "Access level is not defined for '%(option)s'" -msgstr "Razina pristupa nije definirana za '%(option)s'" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Access level is not defined for '%(variable)s'" -msgstr "Razina pristupa nije definirana za '%(variable)s'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__action -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Action" -msgstr "Akcija" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction -msgid "Action Needed" -msgstr "Potrebna radnja" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "Actions" -msgstr "Akcije" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "" -"Actions trigger based on command result. If empty next command will be " -"executed" -msgstr "" -"Pokretač akcije baziran na rezultatu naredbe. Ako je prazno sljedeća naredba " -"će biti izvršena" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__active -msgid "Active" -msgstr "Aktivno" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_ids -msgid "Activities" -msgstr "Aktivnosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "Dekoracija izuzetka aktivnosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "Activity State" -msgstr "Status aktivnosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Activity Type Icon" -msgstr "Ikona tipa aktivnosti" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_action -msgid "Add a new file" -msgstr "Dodaj novu datoteku" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_template_action -msgid "Add a new file template" -msgstr "Dodaj novi predložak" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Allow Parallel Run" -msgstr "Dozvoli paralelno pokretanje" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "An error occurred: %(error)s" -msgstr "Došlo je do greške: %(error)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Another instance of the command is already running" -msgstr "Druga instanca naredbe je već pokrenuta" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__applicability -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__applicability -msgid "Applicability" -msgstr "Primjenjivost" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Archived" -msgstr "Arhivirano" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Are you sure?" -msgstr "Jeste li sigurni?" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_attachment_count -msgid "Attachment Count" -msgstr "Broj priloga" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync -msgid "Auto Sync" -msgstr "Auto sinkronizacija" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync_interval -msgid "Auto Sync Interval" -msgstr "Interval auto sinkronizacije" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Binary" -msgstr "Binarno" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_view_form -msgid "" -"Can contain English letters, digits and '_'. Leave blank to autogenerate" -msgstr "" -"Može sadržavati slova engleske abecede, brojke i ':'. Ostavite prazno za " -"automatsko generiranje" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Cancel" -msgstr "Odustani" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for '%(var)s' with value '%(val)s'.\n" -"Try to assigns it to a record instead." -msgstr "" -"Nije moguće promijeniti status 'global' za '%(var)s' sa vrijednošću " -"'%(val)s'.\n" -"Pokušajte ju dodijeliti nekom zapisu." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for 'meme' with value 'Pepe'.\n" -"Try to assigns it to a record instead." -msgstr "" -"Nije moguće promijeniti 'global' status za 'meme' sa vrijednosti 'Pepe'.\n" -"Pokušajte ju dodijeliti nekom zapisu." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Cannot download %(f)s from server: Binary content is not supported for " -"'Text' file type" -msgstr "" -"Nije moguće preuzeti %(f)s sa servera: Binarni sadržaj nije podržan za " -"datoteke tipa 'Tekst'" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot execute command\n" -". CODE: %(status)s. RESULT: %(res)s. ERROR: %(err)s" -msgstr "" -"Nije moguće izvršiti naredbu\n" -". CODE: %(status)s. RESULT: %(res)s. ERROR: %(err)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Cannot pull %(f)s from server: %(err)s" -msgstr "Nije moguće preuzeti %(f)s sa servera: %(err)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot remove test file using command.\n" -" CODE: %(status)s. ERROR: %(err)s" -msgstr "" -"Nije moguće ukloniti datoteku korištenjem naredbe.\n" -" CODE: %(status)s. ERROR: %(err)s" - -#. module: cetmix_tower_server -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower -#: model:ir.ui.menu,name:cetmix_tower_server.menu_root -msgid "Cetmix Tower" -msgstr "Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command -msgid "Cetmix Tower Command" -msgstr "Cetmix Tower Naredba" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_log -msgid "Cetmix Tower Command Log" -msgstr "Cetmix Tower Log Naredbi" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_command_execute_wizard_action -msgid "Cetmix Tower Execute Command" -msgstr "Cetmix Tower Izvrši naredbu" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_auto_pull_files_from_server_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_auto_pull_files_from_server -#: model:ir.cron,name:cetmix_tower_server.ir_cron_auto_pull_files_from_server -msgid "Cetmix Tower File Management: Auto pull files from server" -msgstr "Cetmix Tower Upravljanje datotekama: Automatski povuci sa servera" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan -msgid "Cetmix Tower Flight Plan" -msgstr "Cetmix Tower Plan Leta" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line -msgid "Cetmix Tower Flight Plan Line" -msgstr "Cetmix Tower Stavka plana leta" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line_action -msgid "Cetmix Tower Flight Plan Line Action" -msgstr "Cetmix Tower Akcija stavke plana leta" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_log -msgid "Cetmix Tower Flight Plan Log" -msgstr "Cetmix Tower Log plana leta" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_os -msgid "Cetmix Tower Operating System" -msgstr "Cetmix Tower Operativni sistem" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_plan_execute_wizard_action -msgid "Cetmix Tower Run Flight Plan" -msgstr "Cetmix Tower pokreni Plan leta" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server -msgid "Cetmix Tower Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_log -msgid "Cetmix Tower Server Log" -msgstr "Cetmix Tower Log servera" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template -msgid "Cetmix Tower Server Template" -msgstr "Cetmix Tower Predložak servera" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_tag -msgid "Cetmix Tower Tag" -msgstr "Cetmix Tower Oznaka" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable -msgid "Cetmix Tower Variable" -msgstr "Cetmix Tower Varijabla" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_option -msgid "Cetmix Tower Variable Options" -msgstr "Cetmix Tower Opcija varijable" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_value -msgid "Cetmix Tower Variable Values" -msgstr "Cetmix Tower Vrijednosti varijabli" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_mixin -msgid "Cetmix Tower access mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_role_mixin -msgid "Cetmix Tower access role mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key -msgid "Cetmix Tower private key storage" -msgstr "Cetmix Tower Pohrana privatnih ključeva" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_reference_mixin -msgid "Cetmix Tower reference mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_template_mixin -msgid "Cetmix Tower template rendering mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Child plans" -msgstr "Podređeni planovi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "Code" -msgstr "Kod" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Code On Server" -msgstr "Kod na serveru" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__color -msgid "Color" -msgstr "Boja" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__command_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Command" -msgstr "Naredba" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Command '%(cmd)s' is not compatible with the server '%(server)s'." -msgstr "Naredba '%(cmd)s' nije kompatabilna sa serverom '%(server)s'." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__code -msgid "Command Code" -msgstr "Kod naredbe" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__command_domain -msgid "Command Domain" -msgstr "Domena naredbe" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command_log -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__command_log_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__command_log_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_log -#, python-format -msgid "Command Log" -msgstr "Log naredbe" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Command Logs" -msgstr "Logovi naredbi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__is_running -msgid "Command is being executed right now" -msgstr "Naredba se upravo izvršava" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__command_id -msgid "" -"Command that will be executed to get the log data.\n" -"Be careful with commands that don't support parallel execution!" -msgstr "" -"Naredba koja će biti izvršena za dohvaćanje podataka loga.\n" -"Budite oprezni sa naredbama koje ne podržavaju paralelno izvršavanje!" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__line_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_line_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_root -msgid "Commands" -msgstr "Naredbe" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__condition -msgid "Condition" -msgstr "Uvijet" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__condition -msgid "" -"Conditions under which this Flight Plan Line will be launched. e.g.: {{ " -"odoo_version}} == '16.0'" -msgstr "" -"Uvijeti pod kojima će se ovaj PlanLeta pokrenuti, nrp: {{ odoo_version }} == " -"'16.0'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Configuration" -msgstr "Konfiguracija" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__line_ids -msgid "Configuration Variables" -msgstr "Varijable konfiguracije" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Confirm" -msgstr "Potvrdi" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection failed." -msgstr "Uspostava veze nije uspjela." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection successful." -msgstr "Veza uspostavljena uspješno." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Connection test passed! \n" -"%(res)s" -msgstr "" -"Test veze prošao! \n" -"%(res)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#, python-format -msgid "Create Server" -msgstr "Kreiraj server" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard -msgid "Create new server from template" -msgstr "Kreiraj novi server iz predloška" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard_line -msgid "Create new server from template variables" -msgstr "Kreiraj novi server iz varijabli predloška" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_uid -msgid "Created by" -msgstr "Kreirao" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_date -msgid "Created on" -msgstr "Kreirano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Custom Exit Code" -msgstr "Prilagođeni kod izlaza" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Custom label. Can be used for search/tracking" -msgstr "Prilagođeni natpis. Može se koristiti za pretraživanje/praćenje" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file -msgid "Cx Tower File" -msgstr "Cx Tower datoteka" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file_template -msgid "Cx Tower File Template" -msgstr "CxTower Predložak datoteke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Date and time of the latest successful synchronisation" -msgstr "Datum i vrijeme posljednje uspješne sinkronizacije" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Date and time of the next synchronisation" -msgstr "Datum i vrijeme sljedeće sinkronizacije" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__path -msgid "Default Path" -msgstr "Zadana putanja" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "Default full file name with file type for example: test.txt" -msgstr "Zadani puni naziv datoteke sa vrstom datoteke: npr: test.txt" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_delete_action -msgid "Delete from server" -msgstr "Obriši sa servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_dir -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__server_dir -msgid "Directory on server" -msgstr "Direktorij na serveru" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server__display_name -msgid "Display Name" -msgstr "Naziv" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_download_action -msgid "Download" -msgstr "Preuzmi" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Due to security restrictions you are not allowed to delete %(fp)s" -msgstr "Zbog sigurnosnih ograničenja nije vam dozvoljeno brisanje: %(fp)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Duration" -msgstr "Trajanje" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "Duration, sec" -msgstr "Trajanje, sek" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Each python code command returns the COMMAND_RESULT value\n" -" which is a dictionary." -msgstr "" -"Svaka naredba tipa python code vraća COMMAND_RESULT vrijednost\n" -" koja je dictionary." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_dir -msgid "Eg '/home/user' or '/var/log'" -msgstr "Npr. '/home/user' ili '/var/log'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Enter Python code here. Help about Python expression is available in the " -"help tab of this document." -msgstr "" -"Unesite python kod ovdje. Pomoć oko Python izraza je dostupna na kartici " -"pomoći ovog dokumenta." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_error -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Error" -msgstr "Greška" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Error loading a private key. Unsupported key format or incorrect key." -msgstr "" -"Greška pri učitavanju privatnog ključa. Nepodržani format ključa ili " -"neispravan ključ." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Execute Command" -msgstr "Izvrši naredbu" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_execute_wizard -msgid "Execute Command in Wizard" -msgstr "Izvrši naredbu u Čarobnjaku" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_execute_wizard -msgid "Execute Flight Plan in Wizard" -msgstr "Izvrši Plan leta u Čarobnjaku" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Execute Result" -msgstr "Izvrši rezultat" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute flight plan error" -msgstr "Greška izvršavanja plana leta" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute flight plan error %(err)s" -msgstr "Greška izvršavanja plana leta: %(err)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Execute python code error: %(err)s" -msgstr "Greška izvršavanja Python coda: %(err)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Execution Path" -msgstr "Putanja izvršavanja" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_status -msgid "Exit Code" -msgstr "Izlazni kod" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__e -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__e -msgid "Exit with command exit code" -msgstr "Izađi iz naredbe sa izlaznim kodom naredbe" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__ec -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__ec -msgid "Exit with custom exit code" -msgstr "Izađi sa prilagođenim izlaznim kodom" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Failed" -msgstr "Neuspješno" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect after %(attempts)s attempts. Error: %(err)s" -msgstr "Neuspješno povezivanje nakon %(attempts)s pokušaja. Greška: %(err)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect. Error: %(err)s" -msgstr "Neuspješno povezivanje. Greška: %(err)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Failure" -msgstr "Neuspjeh" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_id -msgid "File" -msgstr "Datoteka" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"File %(f)s is not 'tower' type. This operation is supported for 'tower' " -"files only" -msgstr "" -"Datoteka %(f)s nije tipa 'tower'. Ova operacija je podržana samo za 'tower' " -"tip datoteka" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"File %(f)s shouldn't have the '%(src)s' source for the '%(act)s' action" -msgstr "Datoteka %(f)s nebi trebala imati '%(src)s' izvor za '%(act)s' akciju" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "File Name" -msgstr "Naziv datoteke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "File Template" -msgstr "Predložak datoteke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "File Type" -msgstr "Tip datoteke" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File already exists" -msgstr "Datoteka već postoji" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file_template.py:0 -#, python-format -msgid "File already exists on server." -msgstr "Datoteka već postoji na serveru." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__code -msgid "File content" -msgstr "Sadržaj datoteke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "File content with variables rendered" -msgstr "Sadržaj datoteke sa vrijednostima varijabli" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File created and uploaded successfully" -msgstr "Datoteka uspješno kreirana i prebačena na server" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File deleted!" -msgstr "Datoteka obrisana!" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File downloaded!" -msgstr "Datoteka preuzeta!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "File from template" -msgstr "Datoteka iz predloška" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "File name" -msgstr "Naziv datoteke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__name -msgid "File name WITHOUT path. Eg 'test.txt'" -msgstr "Naziv datoteke BEZ putanje. Npr: 'test.txt'" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File source cannot be determined: '%(source)s'" -msgstr "Izvor datoteke nije moguće odrediti: '%(source)s'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_id -msgid "File that will be executed to get the log data" -msgstr "Datoteka koja će biti izvršena za dohvaćanje log podataka" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File uploaded!" -msgstr "Datoteka poslana na server!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "File will be disconnected from template. Continue?" -msgstr "Datoteka će raskinuti vezu sa predloškom. Nastaviti?" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "File will be kept on server when deleted in Tower" -msgstr "Datoteka će biti ostavljena na serveru i nakon brisanja na Toweru" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_count -msgid "File(s)" -msgstr "Dototeka(e)" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Files" -msgstr "Datoteke" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files deleted!" -msgstr "Datoteke obrisane!" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files downloaded!" -msgstr "Datoteke preuzete!" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files uploaded!" -msgstr "Datoteke poslane na server!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Finish date" -msgstr "Datum završetka" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__finish_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__finish_date -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Finished" -msgstr "Završeno" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Flight Plan" -msgstr "Plan leta" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_line_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Flight Plan Lines" -msgstr "Stavke Plana leta" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan_log -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan_log -msgid "Flight Plan Log" -msgstr "Log Plana leta" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Flight Plan Logs" -msgstr "Logovi Plana leta" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Flight Plan line that is being currently executed" -msgstr "Plan leta koji se trenutno izvršava" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan -msgid "Flight Plans" -msgstr "Planovi leta" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "Flight plan '%(plan)s' is not compatible with the server '%(server)s'." -msgstr "Plan leta '%(plan)s' nije kompatabilan sa serverom '%(server)s'." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_follower_ids -msgid "Followers" -msgstr "Pratitelji" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_channel_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_channel_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_channel_ids -msgid "Followers (Channels)" -msgstr "Pratitelji (kanali)" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_partner_ids -msgid "Followers (Partners)" -msgstr "Pratitelji (Partneri)" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Font awesome icon e.g. fa-tasks" -msgstr "Font awesome ikona npr. fa-tasks" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__color -msgid "For better visualization in views" -msgstr "Za bolu vizualizaciju u pregledima" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "For how long a flight plan is already running" -msgstr "Koliko dugo je plan već pokrenut" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__applicability__this -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_execute_wizard__applicability__this -msgid "For selected server(s)" -msgstr "Za odabrane server(e)" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__full_server_path -msgid "Full Server Path" -msgstr "Puna putanja servera" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "General Settings" -msgstr "Generalne postavke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__is_global -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Global" -msgstr "Globalno" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Group By" -msgstr "Grupiraj po" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__has_missing_required_values -msgid "Has Missing Required Values" -msgstr "Nedostaju obavezne vrijednosti" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Has Template" -msgstr "Ima predložak" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Help" -msgstr "Pomoć" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Help with Python expressions" -msgstr "Pomoć oko Python izraza" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server__id -msgid "ID" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v4_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v4_address -msgid "IPv4 Address" -msgstr "IPv4 Adresa" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v6_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v6_address -msgid "IPv6 Address" -msgstr "IPv6 Adresa" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon" -msgstr "Ikona" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "Ikona koja indicira izuzetak aktivnosti." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_unread -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_unread -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_unread -msgid "If checked, new messages require your attention." -msgstr "Ako je označeno, nove poruke traže vašu pozornost." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "If checked, some messages have a delivery error." -msgstr "Ako je označeno, neke poruke imaju greške pri isporuci." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -msgid "" -"If enabled command can be run on the same server while the same command is still running.\n" -"Returns ANOTHER_COMMAND_RUNNING if execution is blocked" -msgstr "" -"Ako je omogućeno, naredbe se mogu izvršavati na istom serveru dok se ista " -"naredba već izvršava.\n" -"Vraća ANOTHER_COMMAND_RUNNING ako je izvršavanje blokirano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__auto_sync -msgid "If enabled file will be synced automatically using cron" -msgstr "" -"Ako je omogućeno datoteka će biti sinkronitzirana automatski korištenjem " -"crona" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -msgid "" -"If enabled flightplan can be run on the same server while the same flightplan is still running.\n" -"Returns -5 status is execution is blocked" -msgstr "" -"Ako je označeno Plan leta može biti pokrenut na istom serveru dok je isti " -"plan leta već pokrenut.\n" -"Vraća status -5 ako je izvršavanje blokirano" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#, python-format -msgid "If exit code" -msgstr "Ako je kod izlaza" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_plan.py:0 -#, python-format -msgid "If exit code == 35 then Exit with command exit code" -msgstr "Ako je kod izlaza == 35 tada Izađi sa izlaznim kodom naredbe" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -msgid "Indicates if this variable is mandatory for server creation" -msgstr "Označava da je varijabla obavezna za kreiranje servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_is_follower -msgid "Is Follower" -msgstr "Je pratitelj" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_running -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__is_running -msgid "Is Running" -msgstr "Je pokrenut" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_skipped -msgid "Is Skipped" -msgstr "Je preskočen" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "Keep When Deleted" -msgstr "Zadrži nakon brisanja" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__k -msgid "Key" -msgstr "Ključ" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__key_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Key Type" -msgstr "Tip ključa" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Key reference for inline usage" -msgstr "Referenca ključa za korištenje u kodu" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_key -msgid "Keys and Secrets" -msgstr "Ključevi i Tajne" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Label" -msgstr "Labela" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Labeled" -msgstr "Obilježeno" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cetmix_tower____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_ir_actions_server____last_update -msgid "Last Modified on" -msgstr "Zadnje modificirano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Last Sync Date" -msgstr "Datum posljednje sinkronizacije" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_uid -msgid "Last Updated by" -msgstr "Zadnji ažurirao" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_date -msgid "Last Updated on" -msgstr "Zadnje ažurirano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Latest version of file content on server" -msgstr "Zadnja verzija sadržaja datoteke na serveru" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__partner_id -msgid "Leave blank to use for any partner" -msgstr "Ostavite prazno za korištenje sa bilo kojim partnerom" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Leave blank to use with any partner" -msgstr "Ostavite prazno za korištenje sa bilo kojim partnerom" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Leave blank to use with any server" -msgstr "Ostavite prazno za korištenje na bilo kojem serveru" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__line_id -msgid "Line" -msgstr "Stavka" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Local" -msgstr "Localno" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "" -"Location where command will be executed. Overrides command default path. You" -" can use {{ variables }} in path" -msgstr "" -"Lokacija na kojoj će naredba biti izvršena. Nadjačava zadanu putanju " -"naredbe. Možete koristiti {{ varijable }} u putanji" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__path -msgid "" -"Location where command will be executed. You can use {{ variables }} in path" -msgstr "" -"Lokacija na kojoj će naredba biti izvršena. Možete koristiti {{varijable }} " -"u putanji" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_text -msgid "Log Text" -msgstr "Sadržaj loga" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_type -msgid "Log Type" -msgstr "Tip loga" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "Logs" -msgstr "Logovi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_main_attachment_id -msgid "Main Attachment" -msgstr "Glavni prilog" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__parent_flight_plan_log_id -msgid "Main Log" -msgstr "Glavni log" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plan" -msgstr "Glavni plan" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plans" -msgstr "Glavni planovi" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_manager -msgid "Manager" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers" -msgstr "Manageri" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers who can modify this record" -msgstr "Manageri koji mogu urediti ovaj zapis" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "Message Delivery error" -msgstr "Greška pri isporuci poruke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_ids -msgid "Messages" -msgstr "Poruke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables -msgid "Missing Required Variables" -msgstr "Nedostaju obavezne variable" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables_message -msgid "Missing Required Variables Message" -msgstr "Poruka za nedostajuće obavezne varijable" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key_mixin -msgid "Mixin for managing secrets" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Modify Code" -msgstr "Modificiraj kod" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "Krajnji rok moje aktivnosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__name -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Name" -msgstr "Naziv" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "Krajnji rok sljedeće aktivnosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_summary -msgid "Next Activity Summary" -msgstr "Sažetak sljedeće aktivnosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_id -msgid "Next Activity Type" -msgstr "Tip sljedeće aktivnosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Next Sync Date" -msgstr "Datum sljedeće sinkronizacije" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Synced" -msgstr "Nije sinkronizirano" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Template" -msgstr "Nema predloška" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"No output received. Please log in manually and check for any issues.\n" -"===\n" -"CODE: %(status)s" -msgstr "" -"Rezultat nije primljen. Molimo prijavite se ručno i provjerite ima li " -"problema.\n" -"===\n" -"KOD: %(status)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "No runner found for command action '%(cmd_action)s'" -msgstr "Nije pronađen pokretač za akciju komande '%(cmd_action)s'" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "No server found for the provided reference." -msgstr "Nije pronađen server za upisanu referencu." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "No sudo" -msgstr "Bez sudo" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__applicability__shared -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_execute_wizard__applicability__shared -msgid "Non server restricted" -msgstr "Nema ograničenja za servere" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Not Running" -msgstr "Nije pokrenut" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__note -msgid "Note" -msgstr "Bilješka" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of Actions" -msgstr "Broj akcija" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of errors" -msgstr "Broj grešaka" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of messages which requires an action" -msgstr "Broj poruka koje zahtijevaju neku akciju" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of messages with delivery error" -msgstr "Broj poruka sa greškama pri isporuci" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_unread_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_unread_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_unread_counter -msgid "Number of unread messages" -msgstr "Broj nepročitanih poruka" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_os -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "OS" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__os_ids -msgid "OSes" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_os -msgid "OSs" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_delete_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "On Delete Plan" -msgstr "Plan za brisanje" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "On Error" -msgstr "Nakon Greške" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Only one global value can be defined for variable '%(var)s'" -msgstr "" -"Samo jedna globalna vrijednost može biti definirana za varijablu '%(var)s'" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "Only one global value can be defined for variable 'meme'" -msgstr "Samo jedna globalna vrijednost može biti definirna za varijablu 'meme'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open" -msgstr "Otvori" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open full form" -msgstr "Otvori punu formu" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__os_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__os_id -msgid "Operating System" -msgstr "Operativni sustav" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__option_id -msgid "Option" -msgstr "Opcija" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Option '%(val)s' is not available for variable '%(var)s'" -msgstr "Opcija '%(val)s' nije dostupna za varijablu '%(var)s'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__option_ids -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__o -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Options" -msgstr "Opcije" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__partner_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__partner_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Partner" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__p -msgid "Password" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__path -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "Path" -msgstr "Putanja" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__plan_domain -msgid "Plan Domain" -msgstr "Domena plana" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__plan_line_action_id -msgid "Plan Line Action" -msgstr "Akcija stavke plana" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Plan Line Executed" -msgstr "Stavka plana izvršena" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_plan_execute_wizard.py:0 -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__plan_log_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_log_ids -#, python-format -msgid "Plan Log" -msgstr "Log plana" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__is_running -msgid "Plan is being executed right now" -msgstr "Plan se trenutno izvršava" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Plan line condition check failed." -msgstr "Provjera uvijeta stavke plana nije uspjela." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide IPv4 or IPv6 address for %(srv)s" -msgstr "Molimo upišite IPv4 ili IPv6 adresu za %(srv)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH Key for %(srv)s" -msgstr "Molimo unesite SSH ključ za %(srv)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH password for %(srv)s" -msgstr "Molimo upišite SSH password za %(srv)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py:0 -#, python-format -msgid "" -"Please provide values for the following configuration variables: " -"%(variables)s" -msgstr "" -"Upišite vrijednosti za sljedeće konfiguracijske varijable: %(variables)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "Please resolve the following issues with configuration variables:" -msgstr "Molimo riješite sljedeće probleme sa konfiguracijskim varijablama:" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Please select a command to execute" -msgstr "Molim odaberite naredbu za izvršavanje" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Post Run Actions" -msgstr "Akcije nakon izvršavanja" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Preview" -msgstr "Pregled" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__parent_id -msgid "Previous Version" -msgstr "Prethodna verzija" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Pull from Server" -msgstr "Povuci sa servera" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Push to Server" -msgstr "Prebaci na server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__path -msgid "" -"Put custom path to run the command.\n" -"IMPORTANT: this field does NOT support variables!" -msgstr "" -"Stavite prilagođenu putanju za pokretanje naredbe.\n" -"VAŽNO: ovo polje NE PODRŽAVA varijable!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Put your notes here" -msgstr "Upišite Vaše bilješke ovdje" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Put your notes here..." -msgstr "Stavite vaše bilješke ovdje ..." - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__action__python_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Python code" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Recursive plan call detected in plan %(name)s." -msgstr "Rekurzivni plan detektiran u planu %(name)s." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Reference" -msgstr "Referenca" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Reference Code" -msgstr "Kod reference" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_command_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_file_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_project_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_remote_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_source_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_key_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_os_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_action_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_reference_mixin_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_log_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_tag_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_reference_unique -msgid "Reference must be unique" -msgstr "Referenca mora biti jedinstvena" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_key.py:0 -#, python-format -msgid "Reference must be unique for the combination of partner and server" -msgstr "Referenca mora biti jedinstvena za kombinaciju partnera i servera" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "" -"Reference. Can contain English letters, digits and '_'. Leave blank to " -"autogenerate" -msgstr "" -"Referenca. Može sadržavati slova engleske abecede, brojke i '_'. Ostavite " -"prazno za automatsko generiranje" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -msgid "Refresh" -msgstr "Osvježi" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Refresh All" -msgstr "Osvježi sve" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote\n" -" one." -msgstr "" -"Zapamtite: Python kod se izvršava na Tower serveru, ne na udaljenom\n" -" serveru." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote " -"one." -msgstr "" -"Zapamtite: Python kod se izvršava na Tower serveru, ne na udaljenom serveru." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__rendered_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "Rendered Code" -msgstr "Renderirani kod" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_name -msgid "Rendered Name" -msgstr "Renderirani naziv" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_server_dir -msgid "Rendered Server Dir" -msgstr "Renderirani direktorij na serveru" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__required -msgid "Required" -msgstr "Obavezno" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_response -msgid "Response" -msgstr "Odgovor" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_user_id -msgid "Responsible User" -msgstr "Odgovorni korisnik" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__result -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Result" -msgstr "Rezultat" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_root -msgid "Root" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run" -msgstr "Pokreni" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"Run\n" -" Command" -msgstr "" -"Pokreni\n" -" " -"Naredbu" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"Run\n" -" Flight Plan" -msgstr "" -"Pokreni\n" -" Plan leta" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#, python-format -msgid "Run Command" -msgstr "Pokreni Naredbu" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_plan -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#, python-format -msgid "Run Flight Plan" -msgstr "Pokreni Plan Leta" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run New Command" -msgstr "Pokreni novu naredbu" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run Plan" -msgstr "Pokreni Plan" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Run a flight plan" -msgstr "Pokreni plan leta" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "" -"Run code as it appears in 'Rendered code' in wizard and return to wizard. " -"Result will not be logged" -msgstr "" -"Pokreni kod kako izgleda u 'Renderiranom kodu' u čarobnjaku i vrati se u " -"čarobnjak. Rezultat neće biti upisan u log" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run code using sever method and log result" -msgstr "Pokreni kod korištenjem metode servera i zapiši rezultat u log" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Run command" -msgstr "Pokreni naredbu" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__use_sudo -msgid "Run commands using 'sudo'" -msgstr "Pokreni naredbu koristeći 'sudo'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Run in wizard" -msgstr "Pokreni u čarobnjaku" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__n -msgid "Run next command" -msgstr "Pokreni sljedeću naredbu" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Run on" -msgstr "Pokreni na" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Running" -msgstr "Pokrenut" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Running Now" -msgstr "Izvršava se sada" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_auth_mode -msgid "SSH Auth Mode" -msgstr "Način autentikacije za SSH" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH Client is not defined." -msgstr "SSH clijent nije definiran." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "SSH Command" -msgstr "SSH naredba" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__k -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "SSH Key" -msgstr "SSH ključ" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_key -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "SSH Key / Secret" -msgstr "SSH ključ/tajna" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_password -msgid "SSH Password" -msgstr "SSH password" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__secret_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_key_id -msgid "SSH Private Key" -msgstr "SSH privatni ključ" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "SSH Username" -msgstr "SSH korisničko ime" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__action__ssh_command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "SSH command" -msgstr "SSH naredba" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH connection error %(err)s" -msgstr "Greška SSH povezivanja %(err)s" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH execute command error %(err)s" -msgstr "SSH greška izvršavanja naredbe %(err)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_port -msgid "SSH port" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Search Command Log" -msgstr "Pretraži log naredbi" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Search Commands" -msgstr "Pretraži naredbe" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -msgid "Search File Templates" -msgstr "Pretraži predloške datoteka" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Search Files" -msgstr "Pretraži datoteke" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Search Flight Plan Log" -msgstr "Pretraži planove leta" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Search Flight Plans" -msgstr "Pretraži planove leta" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Search Keys/Secrets" -msgstr "Pretraži Ključeve/Tajne" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_search_view -msgid "Search OS" -msgstr "Pretraži Operativne sisteme" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Search Server Templates" -msgstr "Pretraži predloške servera" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Search Servers" -msgstr "Pretraži servere" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_search_view -msgid "Search Tags" -msgstr "Pretraži oznake" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Search Values" -msgstr "Pretraži vrijednosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__s -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Secret" -msgstr "Tajna" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__secret_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Secrets" -msgstr "Tajne" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__applicability -msgid "" -"Selected server(s): only Commands that are specific to the selected server(s)\n" -"Non server restricted: all Commands that are not specific to any server" -msgstr "" -"Odabrani server(i): Samo naredbe koje su specifične za odabrane servere\n" -"Bez ograničenja servera: sve naredbe koje nisu specifične za bilokoji server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_execute_wizard__applicability -msgid "" -"Selected server(s): only Flight Plans that are specific to the selected server(s)\n" -"Non server restricted: all Flight Plans that are not specific to any server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__sequence -msgid "Sequence" -msgstr "Sekvenca" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Server" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_ir_actions_server -msgid "Server Action" -msgstr "Serverska akcija" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_count -msgid "Server Count" -msgstr "Broj servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_log_ids -msgid "Server Log" -msgstr "Log servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_log_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server Logs" -msgstr "Logovi servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__name -msgid "Server Name" -msgstr "Naziv servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_response -msgid "Server Response" -msgstr "Odgovor servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_status -msgid "Server Status" -msgstr "Status servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_template_id -msgid "Server Template" -msgstr "Predložak servera" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server_template -msgid "Server Templates" -msgstr "Predlošci servera" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server URL, eg 'https://meme.example.com'" -msgstr "URL servera, npr. 'https://meme.example.com'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Server Version" -msgstr "Verzija servera" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Server not found" -msgstr "Server nije pronađen" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_response -msgid "" -"Server response received during the last operation.\n" -"Default value if no error happened is 'ok'.\n" -"Otherwise there will be a server error message logged." -msgstr "" -"Odgovor servera primljen tijekom posljednje operacije.\n" -"Zadana vrijednost ukoliko nije došlo do greške je 'ok'.\n" -"Inače će greška biti upisana u log servera." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Server tight" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__url -msgid "Server web interface, eg 'https://doge.example.com'" -msgstr "Web sučenje servera, npr. 'https://doge.example.com'" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__server_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "Servers" -msgstr "Serveri" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_ids -msgid "" -"Servers on which the command will be executed.\n" -"If empty, command canbe executed on all servers" -msgstr "" -"Serveri na kojima će naredba biti izvršena.\n" -"Ako je prazno, naredba može biti izvršena na svim serverima" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Set Variable Values" -msgstr "Postavite vrijednosti varijabli" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_status -msgid "" -"Set the following status if command is executed successfully. Leave " -"'Undefined' if you don't need to update the status" -msgstr "" -"Postavi sljedeći status ako je naredba izvršena uspješno. Ostavite " -"'Nedefinirano' ako nema potrebe ažurirati status" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_settings -msgid "Settings" -msgstr "Postavke" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "Show Commands" -msgstr "Prikaži naredbe" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "Show Flight Plans" -msgstr "Prikaži Planove leta" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__show_servers -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__show_servers -msgid "Show Servers" -msgstr "Prikaži servere" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Skipped" -msgstr "Preskočeno" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "Some servers don't support this command" -msgstr "Neki serveri ne podržavaju ovu naredbu" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Some variable options are invalid:\n" -"%(detailed_message)s" -msgstr "" -"Neke vrijednosti varijabli nisu valjane:\n" -"%(detailed_message)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__source -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__source -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Source" -msgstr "Izvor" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Start date" -msgstr "Početni datum" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__start_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__start_date -msgid "Started" -msgstr "Pokrenuto" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_status -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__status -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Status" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "" -"Status based on activities\n" -"Overdue: Due date is already passed\n" -"Today: Activity date is today\n" -"Planned: Future activities." -msgstr "" -"Status baziran na aktivnostima\n" -"Prekoračen rok: Krajnji datum je već prošao\n" -"Danas: Datum aktivnosti je danas\n" -"Planirano: Buduće aktivnosti." - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__s -msgid "String" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#, python-format -msgid "Success" -msgstr "Uspješno" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__use_sudo__p -msgid "Sudo with password" -msgstr "Sudo sa passwordom" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_execute_wizard__use_sudo__n -msgid "Sudo without password" -msgstr "Sudo bez passworda" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Sync Error" -msgstr "Greška sinkronizacije" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Synced" -msgstr "Sinkronizirano" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_tag -msgid "Tag" -msgstr "Oznaka" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Tagged" -msgstr "Označeno" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_execute_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__tag_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_tag -msgid "Tags" -msgstr "Oznake" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Template" -msgstr "Predložak" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Template Code" -msgstr "Šifra predloška" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_template_action -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_template -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_template -msgid "Templates" -msgstr "Predlošci" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Test Connection" -msgstr "Test povezivanja" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Text" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "" -"The access level for Variable Option '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Option Access Level: %(val_level)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"The access level for Variable Value '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Value Access Level: %(val_level)s" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "" -"The access level of command(s) '%(command_names)s' included in the current " -"Flight plan is higher than the access level of the Flight plan itself. " -"Please ensure that you want to allow those commands to be run anyway." -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_unique_variable_option -msgid "The combination of Name,Value and Variable must be unique." -msgstr "Kombinacija Naziv, Vrijednost i Varijabla mora biti jedinstvena." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "The file %(f_path)s not found." -msgstr "Datoteka %(f_path)s nije pronađena." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Then" -msgstr "Onda" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "There are two default keys in the dictionary, e.g.:" -msgstr "Postoje dva zadana ključa u dictionary.u npr:" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__plan_delete_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "This Flightplan will be executed when the server is deleted" -msgstr "Ovaj plan leta će biti izvršen kada je server obrisan" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "" -"This action will be executed on error if no command action can be applied" -msgstr "" -"Ova će akcija biti izvršena prilikom greške ako niti jedna druga akcija nije " -"primjenjiva" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "This command can be used only in Flight Plans." -msgstr "Ova naredba se može koristiti samo u Planovima leta." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__note -msgid "This field is used to put some notes regarding template." -msgstr "Ovo polje koristi se za upisivanje bilješki vezanih za predložak." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_execute_wizard__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_template_mixin__code -msgid "This field will be rendered by default" -msgstr "Ovo polje će biti generirano prema postavkama" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_template_id -msgid "" -"This file template will be used to create log files when server is created " -"from a template" -msgstr "" -"Ovaj predložak datoteke će biti korišten za kreiranje log datoteke kad je " -"server kreiran iz predloška" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -msgid "This flight plan will be run upon server creation" -msgstr "Ovaj plan leta će biti pokrenut prilikom kreiranja servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "" -"This is required, however you can change this later in the server settings" -msgstr "" -"Ovo je obavezno, ali možete vrijednost promijeniti kasnije u postavkama " -"servera" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -msgid "This template will be used to create or update the pushed file" -msgstr "" -"Ovaj predložak će biti korišten za kreiranje ili ažuriranje datoteke koju se " -"prebacuje na server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Time consumed for execution, seconds" -msgstr "Vrije izvršavanja u sekundama" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_tools -msgid "Tools" -msgstr "Alati" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_count -msgid "Total Files" -msgstr "Ukupno datoteka" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__tower -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__tower -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Tower" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_mixin -msgid "Tower Variables mixin" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cetmix_tower -msgid "Tower automation helper model" -msgstr "" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__triggered_plan_log_id -msgid "Triggered Plan Log" -msgstr "Log pokrenutih planova" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_type -msgid "Type" -msgstr "Tip" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Type of the exception activity on record." -msgstr "Tip aktivnosti izuzetka na zapisu." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__url -msgid "URL" -msgstr "" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to delete file '%(f)s'.\n" -"Delete operation is not supported for 'server' type files." -msgstr "" -"Nije moguće obrisati datoteku '%(f)s'.\n" -"Operacija brisanja nije podržana za tip datoteke 'server'." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to upload file '%(f)s'.\n" -"Upload operation is not supported for 'server' type files." -msgstr "" -"Nije moguće slanje datoteke '%(f)s' na server.\n" -"Operacija slanja nije podržana za datoteku tipa 'server'." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_unread -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_unread -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_unread -msgid "Unread Messages" -msgstr "Nepročitane poruke" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_unread_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_unread_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_unread_counter -msgid "Unread Messages Counter" -msgstr "Brojač nepročitanih poruka" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_upload_action -msgid "Upload" -msgstr "Pošalji na server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__use_sudo -msgid "Use Sudo" -msgstr "Koristi sudo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__use_sudo -msgid "Use sudo" -msgstr "Koristi sudo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH Key" -msgstr "Korišteno kao SSH ključ" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH key in the following servers" -msgstr "Koristi se kao SSH ključ na sljedećim serverima" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Used for" -msgstr "Koristi se za" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__server_id -msgid "Used for selected server only. Leave blank to use globally" -msgstr "" -"Koristi se samo za odabrane server. Ostavite prazno za globalno korištenje" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_user -msgid "User" -msgstr "Korisnik" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users" -msgstr "Korisnici" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users who can view this record" -msgstr "Korisnici koji mogu vidjeti ovaj zapis" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Value" -msgstr "Vrijednost" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids_count -msgid "Value Count" -msgstr "Broj vrijednosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Values" -msgstr "Vrijednosti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Variable" -msgstr "Varijabla" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Variable '%(var)s' can only be assigned to one of the models at a time: " -"Server, Server Template, or Plan Line Action." -msgstr "" -"Varijabla '%(var)s' može biti dodijeljena samo jednom modelu istovremena: " -"Server, Predložak servera, Akcija plana leta." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -msgid "Variable Reference" -msgstr "Referenca varijable" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable_value -#, python-format -msgid "Variable Values" -msgstr "Vrijednosti varijable" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_tower_variable_value_uniq -msgid "Variable can be declared only once for the same record!" -msgstr "Varijabla može biti deklarirana samo jednom za isti zapis!" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_name_uniq -msgid "Variable names must be unique" -msgstr "Nazivi varijabli moraju biti jedinstveni" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable not found" -msgstr "Varijabla nije pronađena" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Variable reference '%(var_ref)s' has an invalid option reference " -"'%(opt_ref)s'." -msgstr "" -"Referenca varijable '%(var_ref)s' ima neispravnu referencu opcije " -"'%(opt_ref)s'." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value created" -msgstr "Vrijednost varijable kreirana" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value updated" -msgstr "Vrijednost varijable ažurirana" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -msgid "Variable values for selected record" -msgstr "Vrijednosti varijabli za odabrani zapis" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_execute_wizard__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable -msgid "Variables" -msgstr "Varijable" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Various fields may use Python code or Python expressions. The\n" -" following variables can be used:" -msgstr "" -"Različita polja mogu koristiti python kod ili python izraze. Moguće je\n" -" " -"koristiti sljedeće varijable:" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Where command was executed" -msgstr "Gdje je naredba izvršena" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Will be used instead of the command exit code" -msgstr "Će biti korišten umjesto izlaznog koda naredbe" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__use_sudo -msgid "" -"Will use sudo based on server settings.If no sudo is configured will run " -"without sudo" -msgstr "" -"Koristi se sudo bazirano na postavkama server. Ako sudo nije postavljen " -"izvršava se bez sudo" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__p -msgid "With password" -msgstr "Sa passwordom" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__n -msgid "Without password" -msgstr "Bez passworda" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__wizard_id -msgid "Wizard" -msgstr "Čarobnjak" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "Wrong action" -msgstr "Pogrešna akcija" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You are not allowed to execute commands in wizard" -msgstr "Nije vam dozvoljeno izvršavati naredbe u čarobnjaku" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You cannot execute an empty command" -msgstr "Nije moguće izvršiti praznu naredbu" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_execute_wizard.py:0 -#, python-format -msgid "You cannot run custom code on multiple servers at once." -msgstr "Nije moguće pokrenuti prilagođeni kod na više servera odjednom." - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/ir_actions_server.py:0 -#, python-format -msgid "" -"You need to have 'write' access to all servers you want to run this action " -"on." -msgstr "" -"Potrebno vam je pravo pisanja na svim serverima na kojima želite pokrenuti " -"ovu akciju." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -msgid "e.g. /home/user This field does NOT support variables" -msgstr "npr. /home/user Ovo polje NE PODRŽAVA varijable" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "e.g. /such/much/{{ path }}, overrides command path" -msgstr "npr. /tamo/neki/{{path}} , nadjačava putanju varijable" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/tests/common.py:0 -#: code:addons/cetmix_tower_server/tests/common.py:0 -#, python-format -msgid "groups_ref must be string or list of strings!" -msgstr "groups_ref mora biti znakovni izraz ili lista znakovnih izraza!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "managers who can modify this record" -msgstr "manageri koji mogu modificirati ovaj zapis" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "managers who can modify this server" -msgstr "manageri koji mogu modificirati ovaj sevrer" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "managers who can modify this template" -msgstr "manageri koji mogu modificirati ovaj predložak" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "new server name" -msgstr "naziv novog servera" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "optional, eg /home/{{ tower.server.username }}" -msgstr "opcionalno, npr. /home/{{ tower.server.username }}" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "sudo password was not provided!" -msgstr "sudo password nije unešen!" - -#. module: cetmix_tower_server -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "then" -msgstr "onda" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "this can be changed later" -msgstr "ovo može biti izmjenjeno kasnije" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "users who can access this server" -msgstr "korisnici koji mogu pristupiti ovom serveru" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "users who can access this template" -msgstr "korisnici koji mogu pristupiti ovom predlošku" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "users who can view this record" -msgstr "korisnici koji mogu vidjeti ovaj zapis" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_execute_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_execute_wizard_view_form -msgid "with tags" -msgstr "sa oznakama" diff --git a/addons/cetmix_tower_server/i18n/it.po b/addons/cetmix_tower_server/i18n/it.po deleted file mode 100644 index 68f1ee9..0000000 --- a/addons/cetmix_tower_server/i18n/it.po +++ /dev/null @@ -1,5356 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * cetmix_tower_server -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2025-11-14 09:18+0100\n" -"Last-Translator: \n" -"Language-Team: Italian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: it\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Poedit 2.3\n" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__source -msgid "" -"\n" -" - Tower: file is pushed from Tower to server.\n" -" - Server: file is pulled from server to Tower.\n" -" " -msgstr "" -"\n" -" - Tower: il file è inviato da Tower al server.\n" -" - Server: il file è scaricato dal server a Tower.\n" -" " - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_user -msgid "" -"\n" -" Basic actions for selected servers.\n" -" " -msgstr "" -"\n" -" Azioni base per i server selezionati.\n" -" " - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_manager -msgid "" -"\n" -" Create and modify selected servers.\n" -" " -msgstr "" -"\n" -" Crea e modifica i server selezionati.\n" -" " - -#. module: cetmix_tower_server -#: model:res.groups,comment:cetmix_tower_server.group_root -msgid "" -"\n" -" Full control over all servers.\n" -" " -msgstr "" -"\n" -" Controllo completo di tutti i server.\n" -" " - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/constants.py:0 -#, python-format -msgid "" -"\n" -"

Help with Python expressions

\n" -"
\n" -"

\n" -" Each Python code command returns the result value which is a dictionary.\n" -"
There are two keys in the dictionary:\n" -"

    \n" -"
  • exit_code: Integer. Exit code of the command. \"0\" means success, any other value means failure. Default value is \"0\".
  • \n" -"
  • message: String. Message to be logged. Default value is \"None\".
  • \n" -"
\n" -"You can also access the custom_values dictionary that contains custom values provided to the command or flight plan.\n" -"Custom values can be modified, thus can be used to pass data between commands in a flight plan.\n" -"Please keep in mind that custom values are persistent only between commands in a flight plan and are not saved to the database.\n" -"
\n" -"Here is an example of a python code command:\n" -"\n" -"\n" -" server_name = server.name\n" -" build_name = custom_values.get(\"build_name\")\n" -" if build_name:\n" -" result = {\"exit_code\": 0, \"message\": \"Build name for \" + server_name + \" is \" + build_name}\n" -" else:\n" -" result = {\"exit_code\": 0, \"message\": \"No build name provided for \" + server_name}\n" -" custom_values[\"build_name\"] = \"New build name\"\n" -"\n" -"

\n" -"
\n" -"Please refer to the official documentation for more information and examples.\n" -"
\n" -"Various fields may use Python code or Python expressions. The\n" -" following variables can be used:

\n" -msgstr "" -"\n" -"

Aiuto con le espressioni Python

\n" -"
\n" -"

\n" -" Ogni comando di codice Python restituisce il valore result che è un dizionario.\n" -"
Ci sono due chiavi nel dizionario:\n" -"

    \n" -"
  • exit_code: Inteero. Codice uscita del comando. \"0\" significa successo, altri valori significano fallimento. Il valore predefinito è \"0\".
  • \n" -"
  • message: Stringa. Messagio da registrare. Il valore predefinito è \"None\".
  • \n" -"
\n" -"Si può anche accedere al dizionario custom_values che contiene calori personalizzati forniti al comando o al piano di volo.\n" -"i valori personalizzati possono essere modificati, quindi possno essere usati per passare dati tra comandi in un piano di volo.\n" -"Tenere presente che i valori personalizzati sono persistenti solo tra comandi in un piano di volo e non sono salvati nel database.\n" -"
\n" -"Qui un esempio di un codice comando Python:\n" -"\n" -"\n" -" server_name = server.name\n" -" build_name = custom_values.get(\"build_name\")\n" -" if build_name:\n" -" result = {\"exit_code\": 0, \"message\": \"Il nome creato per \" + server_name + \" è \" + build_name}\n" -" else:\n" -" result = {\"exit_code\": 0, \"message\": \"Nessun nome creato per \" + server_name}\n" -" custom_values[\"build_name\"] = \"Creazione nuovo nome\"\n" -"\n" -"

\n" -"
\n" -"Fare riferimento alla documentazione ufficiale per ulteriori informazioni ed esmpi.\n" -"
\n" -"Diversi campi possono usare codice Python o espressioni Python.\n" -" Possono essere usate le seguenti variabili:

\n" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Empty values for variables: %(variables)s" -msgstr " - Valori vuoti per le variabili: %(variables)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid " - Missing variables: %(variables)s" -msgstr " - Variabili mancanti: %(variables)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/constants.py:0 -#, python-format -msgid "" -"# Please refer to the 'Help' tab and documentation for more information.\n" -"#\n" -"# You can return command result in the 'result' variable which is a dictionary:\n" -"# result = {\"exit_code\": 0, \"message\": \"Some message\"}\n" -"# default value is {\"exit_code\": 0, \"message\": None}\n" -msgstr "" -"# Per ulteriori informazioni, consultare la scheda \"Aiuto\" e la documentazione.\n" -"#\n" -"# È possibile restituire il risultato del comando nella variabile \"result\", che è un dizionario:\n" -"# risultato = {\"exit_code\": 0, \"message\": \"Some message\"}\n" -"# il valore predefinito è {\"exit_code\": 0, \"message\": None}\n" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_reference_mixin.py:0 -#, python-format -msgid "%(name)s (copy %(number)s)" -msgstr "%(name)s (copia %(number)s)" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_reference_mixin.py:0 -#, python-format -msgid "%(name)s (copy)" -msgstr "%(name)s (copia)" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_shortcut.py:0 -#, python-format -msgid "%(shr)s triggered" -msgstr "attivato %(shr)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "...save record to see the final expression or click the line to edit" -msgstr "... salvare il record per vedere l'espressione finale o fare clic sulla riga per modificare" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__command_status -msgid "" -"0 if command finished successfully.\n" -"-100 general error,\n" -"-101 not found,\n" -"-201 another instance of this command is running,\n" -"-202 no runner found for the command action,\n" -"-203 Python code execution failed\n" -"-205 plan line condition check failed\n" -"503 if SSH connection error occurred\n" -"601 if queue job failed" -msgstr "0 se il comando è stato completato correttamente.-100 errore generale,-101 non trovato,-201 un'altra istanza di questo comando è in esecuzione,-202 nessun runner trovato per l'azione del comando,-203 esecuzione del codice Python non riuscita,-205 controllo delle condizioni della riga del piano non riuscito,503 se si è verificato un errore di connessione SSH,601 se il processo in coda non è riuscito." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__plan_status -msgid "" -"0 if plan is finished successfully. \n" -"-301 if another instance of this flight plan is running, \n" -"-302 if plan is empty, \n" -"-303 if plan reference is missing, \n" -"-304 if plan line reference is missing, \n" -"-306 if plan is not compatible with server,\n" -"-308 if plan is stopped by user" -msgstr "" -"0 se il piano è completato correttamente.\n" -"-301 se un'altra istanza di questo piano di volo è in esecuzione,\n" -"-302 se il piano è vuoto,\n" -"-303 se manca il riferimento al piano,\n" -"-304 se manca il riferimento alla riga del piano\n" -"-306 se il piano non è compatibile con il server-308 se il piano è fermato dall'utente" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "AND" -msgstr "E" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "" -"\n" -" &nbsp;" -msgstr "" -"\n" -" &nbsp;" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_log.py:0 -#, python-format -msgid "" -msgstr "" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "sudo" -msgstr "sudo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Fill in required configuration variables on the “Configuration Values” tab before you can run the command." -msgstr "Bisogna compilare le variabili di configurazione richieste nella sezione \"Valori configurazione\" prima di poter eseguire il comando." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "IPv4 Address:" -msgstr "Indirizzo IPv4:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "IPv6 Address:" -msgstr "Indirizzo IPv6:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Operating System:" -msgstr "Sistema operativo:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "Partner:" -msgstr "Partner:" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -msgid "Servers:" -msgstr "Server:" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "A helper shortcut to env['cx.tower.command']" -msgstr "Una scorciatoia a env['cx.tower.command']" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "A helper shortcut to env['cx.tower.plan']" -msgstr "Una scorciatoia a env['cx.tower.plan']" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "A helper shortcut to env['cx.tower.server']" -msgstr "Una scorciatoia a env['cx.tower.server']" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_action -msgid "A variable value cannot be assigned multiple times to the same plan line action!" -msgstr "Non è possibile assegnare più volte alla stessa azione della linea del piano un valore variabile!" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_template -msgid "A variable value cannot be assigned multiple times to the same server template!" -msgstr "Un valore di variabile non può essere assegnato più volte allo stesso modello di server!" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_server -msgid "A variable value cannot be assigned multiple times to the same server!" -msgstr "Un valore variabile non può essere assegnato più volte allo stesso server!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Access" -msgstr "Accesso" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_mixin__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__access_level -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__access_level -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Access Level" -msgstr "Livello accesso" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__access_level_warn_msg -msgid "Access Level Warn Msg" -msgstr "Messaggio avviso livello accesso" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "Access level is not defined for '%(option)s'" -msgstr "Il livello di accesso non è definito per '%(option)s'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Access level is not defined for '%(variable)s'" -msgstr "Il livello di accesso non è definito per '%(variable)s'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__action -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Action" -msgstr "Azione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction -msgid "Action Needed" -msgstr "Richiesta una azione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "Actions" -msgstr "Azioni" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__action_ids -msgid "Actions trigger based on command result. If empty next command will be executed" -msgstr "Le azioni vengono attivate in base al risultato del comando. Se vuoto, verrà eseguito il comando successivo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__active -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__active -msgid "Active" -msgstr "Attivo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_ids -msgid "Activities" -msgstr "Attività" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "Decorazione eccezione attività" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "Activity State" -msgstr "Stato attività" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Activity Type Icon" -msgstr "Icona tipo attività" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_action -msgid "Add a new file" -msgstr "Aggiungi nuovo file" - -#. module: cetmix_tower_server -#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_file_template_action -msgid "Add a new file template" -msgstr "Aggiungi nuovo modello file" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__note -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__note -msgid "" -"Additional notes about the variable. \n" -"This field will be displayed in the variable form." -msgstr "" -"Note aggiuntive sulla variabile.\n" -"Questo campo verrà visualizzato nel modulo della variabile." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -msgid "All" -msgstr "Tutti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Allow Parallel Run" -msgstr "Consenti esecuzione in parallelo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "An error occurred: %(error)s" -msgstr "Si è verificato un errore: %(error)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Another instance of the command is already running" -msgstr "Un'altra istanza del comando è già in esecuzione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__applicability -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__applicability -msgid "Applicability" -msgstr "Applicabilità" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__applied_expression -msgid "Applied Expression" -msgstr "Espressione applicata" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Archived" -msgstr "In archivio" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Are you sure?" -msgstr "Si è sicuri?" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_attachment_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_attachment_count -msgid "Attachment Count" -msgstr "Conteggio allegati" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__auto_sync -msgid "Auto Sync" -msgstr "Sincronizzazione automatica" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__auto_sync_interval -msgid "Auto Sync Interval" -msgstr "Intervallo sincronizzazione automatica" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__value_char -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__value_char -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__value_char -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task_cv__value_char -msgid "Automatically populated from selected option. Manual edits will be overwritten when option changes." -msgstr "Compilato automaticamente in base all'opzione selezionata. Le modifiche manuali verranno sovrascritte quando cambia l'opzione." - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_automation_root -msgid "Automation" -msgstr "Automazione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Binary" -msgstr "Binario" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file -msgid "Binary Content" -msgstr "Contenuto binario" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key_value__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_shortcut__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_view_form -msgid "Can contain English letters, digits and '_'. Leave blank to autogenerate" -msgstr "Può contenere lettere inglesi, cifre e '_'. Lasciare vuoto per la generazione automatica" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Cancel" -msgstr "Annulla" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for '%(var)s' with value '%(val)s'.\n" -"Try to assigns it to a record instead." -msgstr "" -"Impossibile modificare lo stato 'global' per '%(var)s' con valore '%(val)s'. \n" -"Provare ad assegnarlo ad un record." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "" -"Cannot change 'global' status for 'meme' with value 'Pepe'.\n" -"Try to assigns it to a record instead." -msgstr "" -"Impossibile modificare lo stato 'global' per 'meme' con valore 'Pepe'. \n" -"Provare ad assegnarlo ad un record." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_tag.py:0 -#, python-format -msgid "Cannot delete tag '%(tag_name)s' because it is used in related records." -msgstr "Impossibile eliminare l'etichetta '%(tag_name)s' perché è utilizzata nei record correlati." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Cannot download %(f)s from server: Binary content is not supported for 'Text' file type" -msgstr "Impossibile scaricare %(f)s dal server: il contenuto binario non è supportato per il tipo di file 'Text'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Cannot pull %(f)s from server: %(err)s" -msgstr "Impossibile scaricare %(f)s dal server: %(err)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot remove test file using command.\n" -" CODE: %(status)s. ERROR: %(err)s" -msgstr "" -"Non si può rimuovere il file test usando il comando.\n" -" CODE: %(status)s. ERROR: %(err)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Cannot run command\n" -". CODE: %(status)s. RESULT: %(res)s. ERROR: %(err)s" -msgstr "" -"Impossibile eseguire il comando\n" -". CODE: %(status)s. RESULT: %(res)s. ERROR: %(err)s" - -#. module: cetmix_tower_server -#: model:ir.module.category,name:cetmix_tower_server.ir_module_category_tower -#: model:ir.ui.menu,name:cetmix_tower_server.menu_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_res_partner_form_inherit_cetmix_tower -msgid "Cetmix Tower" -msgstr "Cetmix Tower" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Cetmix Tower helper class shortcut" -msgstr "Scorciatoia classe helper di Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command -msgid "Cetmix Tower Command" -msgstr "Comando Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_log -msgid "Cetmix Tower Command Log" -msgstr "Registro comando Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file -msgid "Cetmix Tower File" -msgstr "File Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_file_template -msgid "Cetmix Tower File Template" -msgstr "Modello file Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan -msgid "Cetmix Tower Flight Plan" -msgstr "Piano di volo Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line -msgid "Cetmix Tower Flight Plan Line" -msgstr "Riga piano di volo Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_line_action -msgid "Cetmix Tower Flight Plan Line Action" -msgstr "Azione riga piano di volo Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_log -msgid "Cetmix Tower Flight Plan Log" -msgstr "Registro piano di volo Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key_mixin -msgid "Cetmix Tower Key/Secret Mixin" -msgstr "Mixin chiave/segreto Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key -msgid "Cetmix Tower Key/Secret Storage" -msgstr "Deposito chiave/segreto Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cetmix_tower -msgid "Cetmix Tower Odoo Automation" -msgstr "Automazione Odoo Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_os -msgid "Cetmix Tower Operating System" -msgstr "Sistema operativo Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_command_run_wizard_action -msgid "Cetmix Tower Run Command" -msgstr "Esegui comando Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_plan_run_wizard_action -msgid "Cetmix Tower Run Flight Plan" -msgstr "Cetmix Tower esegue piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_key_value -msgid "Cetmix Tower Secret Value Storage" -msgstr "Deposito valore segreto Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "Cetmix Tower Server" -msgstr "Server Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_log -msgid "Cetmix Tower Server Log" -msgstr "Registro server Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template -msgid "Cetmix Tower Server Template" -msgstr "Modello server Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_shortcut -msgid "Cetmix Tower Shortcut" -msgstr "Scorciatoia Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_tag -msgid "Cetmix Tower Tag" -msgstr "Etichetta Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_tag_mixin -msgid "Cetmix Tower Tag Mixin" -msgstr "Mixin etichetta Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable -msgid "Cetmix Tower Variable" -msgstr "Variabile Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_option -msgid "Cetmix Tower Variable Options" -msgstr "Opzioni variabile Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_value -msgid "Cetmix Tower Variable Values" -msgstr "Valori variabile Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_vault -msgid "Cetmix Tower Vault" -msgstr "Valore Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_vault_mixin -msgid "Cetmix Tower Vault Mixin" -msgstr "Mixin valore Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_mixin -msgid "Cetmix Tower access mixin" -msgstr "Mixin accesso Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_access_role_mixin -msgid "Cetmix Tower access role mixin" -msgstr "Mixin ruolo accesso Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_reference_mixin -msgid "Cetmix Tower reference mixin" -msgstr "Mixin riferimento Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_template_mixin -msgid "Cetmix Tower template rendering mixin" -msgstr "Mixin compilazione modello Cetmix Tower" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_auto_pull_files_from_server_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_auto_pull_files_from_server -msgid "Cetmix Tower: Auto pull files from server" -msgstr "Cetmix Tower: scarica automaticamente i file dal server" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_check_zombie_commands_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_check_zombie_commands -msgid "Cetmix Tower: Check zombie commands" -msgstr "Cetmix Tower: controlla comandi zombie" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_run_scheduled_tasks_ir_actions_server -#: model:ir.cron,cron_name:cetmix_tower_server.ir_cron_run_scheduled_tasks -msgid "Cetmix Tower: Run scheduled tasks" -msgstr "Cetmix Tower: esegui lavori schedulati" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_shortcut.py:0 -#, python-format -msgid "Check %(t)s log for result" -msgstr "Controllare registrazione %(t)s per il risultato" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -msgid "Check the key before inserting in the server settings. Do not insert the key if you have any doubts!" -msgstr "Controllare la chiave prima di inserirla nelle impostazioni del server. Non inserirla in caso di dubbi!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Child plans" -msgstr "Piani figli" - -#. module: cetmix_tower_server -#. odoo-javascript -#: code:addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -#, python-format -msgid "Close" -msgstr "Chiudi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Code" -msgstr "Codice" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Code On Server" -msgstr "Codice sul server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__template_code -msgid "Code of the associated file template" -msgstr "Codice del modello file associato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__color -msgid "Color" -msgstr "Colore" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__command_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__command_ids -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__action__command -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_shortcut__action__command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Command" -msgstr "Comando" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "Command %(command_name)s is not compatible with the server" -msgstr "Il comando %(command_name)s non è compatibile con il server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__code -msgid "Command Code" -msgstr "Comando codice" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__command_ids_count -msgid "Command Count" -msgstr "Comando conteggio" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__command_domain -msgid "Command Domain" -msgstr "Comando dominio" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__command_help -msgid "Command Help" -msgstr "Comando aiuto" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_command_log -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__command_log_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__command_log_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_log -#, python-format -msgid "Command Log" -msgstr "Comando registro" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Command Logs" -msgstr "Comando registri" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_config_settings__cetmix_tower_command_timeout -msgid "Command Timeout" -msgstr "Comando timeout" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__command_variable_ids -msgid "Command Variables" -msgstr "Comando variabili" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__code -msgid "Command code that was executed" -msgstr "Codice comando eseguito" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__is_running -msgid "Command is being executed right now" -msgstr "Il comando è appena stato eseguito" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Command is not compatible with the server" -msgstr "Il comando non è compatibile con il server" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Command not found" -msgstr "Comando non trovato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__command_id -msgid "" -"Command that will be executed to get the log data.\n" -"Be careful with commands that don't support parallel execution!" -msgstr "" -"Comando che verrà eseguito per ottenere i dati del log. \n" -"Fare attenzione ai comandi che non supportano l'esecuzione parallela!" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/constants.py:0 -#, python-format -msgid "Command timed out and was terminated" -msgstr "Comando oltre timeout e terminato" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "" -"Command timeout in seconds after which the command will be terminated. Set to 0 to disable timeout.\n" -"
" -msgstr "" -"Timeout del comando in secondi, dopo il quale il comando verrà terminato. Impostare su 0 per disabilitare il timeout..\n" -"
" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__command_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__plan_line_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__command_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__command_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_command_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Commands" -msgstr "Comandi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__command_ids -msgid "Commands used in this flight plan" -msgstr "Comandi usati in questo piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__condition -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__condition -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Condition" -msgstr "Condizione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__condition -msgid "Conditions under which this Flight Plan Line will be launched. e.g.: {{ odoo_version}} == '14.0'" -msgstr "Condizioni in base alle quali verrà lanciato questo piano di volo. e.g.: {{ odoo_version}} == '14.0'" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_res_config_settings -msgid "Config Settings" -msgstr "Impostazioni configurazione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Configuration" -msgstr "Configurazione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Configuration Values" -msgstr "Valori configurazione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__line_ids -msgid "Configuration Variables" -msgstr "Variabili configurazione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "Confirm" -msgstr "Conferma" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection failed." -msgstr "Connessione fallita." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Connection successful." -msgstr "Connessione riuscita." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"Connection test passed! \n" -"%(res)s" -msgstr "" -"Test connessione riuscito! \n" -"%(res)s" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_res_partner -msgid "Contact" -msgstr "Contatto" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_kanban -#, python-format -msgid "Create Server" -msgstr "Crea server" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard -msgid "Create new server from template" -msgstr "Crea nuovo server da modello" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_template_create_wizard_line -msgid "Create new server from template variables" -msgstr "Crea nuovo server da variabili modello" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__create_uid -msgid "Created by" -msgstr "Creato da" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__create_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__create_date -msgid "Created on" -msgstr "Creato il" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/res_config_settings.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -#, python-format -msgid "Cron Job" -msgstr "Lavoro cron" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/res_config_settings.py:0 -#, python-format -msgid "Cron job not found" -msgstr "Lavoro cron non trovato" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Current Cetmix Tower server this command is running on" -msgstr "Server Cetmix Tower corrente su cui è in esecuzione questo comando" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Current Odoo user" -msgstr "Utente Odoo attuale" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Current Odoo user ID" -msgstr "ID utente Odoo attuale" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Custom Exit Code" -msgstr "Codice uscita personalizzato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__custom_message -msgid "Custom Message" -msgstr "Messaggio personalizzato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__custom_variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__custom_variable_value_ids -msgid "Custom Variable Value" -msgstr "Valore variabile personalizzata" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__custom_variable_value_ids -msgid "Custom Variable Values" -msgstr "Valori variabile personalizzata" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Custom label. Can be used for search/tracking" -msgstr "Etichetta personalizzata. Può essere utilizzata per ricerca/tracciamento" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__custom_message -msgid "Custom message to be displayed in the plan log" -msgstr "Messaggio personalizzato da visualizzare nel registro del piano" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Custom message to display when pattern check fails" -msgstr "Messaggio personalizzato da visualizzare quando il controllo del pattern fallisce" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Custom secret values can be defined only for key type 'secret'" -msgstr "I valori segreti personalizzati possono essere definiti solo per il tipo di chiave 'secret'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -msgid "Custom values will be available in the current flight plan context only. They can be used in Python commands and line conditions" -msgstr "I valori personalizzati saranno disponibili solo nel context del piano di volo attuale. Possono esse usati nei comenfdi Python e nelle righe di condizione." - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_custom_variable_value_mixin -msgid "Custom variable values" -msgstr "Valori variabile personalizzata" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_run_wizard_variable_value -msgid "Custom variable values for command run wizard" -msgstr "Valori delle variabili personalizzate per la procedura guidata di esecuzione dei comandi" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_run_wizard_variable_value -msgid "Custom variable values for plan run wizard" -msgstr "Valori delle variabili personalizzate per la procedura guidata di esecuzione del piano" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_scheduled_task_cv -msgid "Custom variable values for scheduled tasks" -msgstr "Valori variabile personalizzata per lavori schedulati" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__variable_values -msgid "Custom variable values passed to the command" -msgstr "Valori delle variabili personalizzate passati al comando" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__variable_values -msgid "Custom variable values passed to the flight plan" -msgstr "Valori variabile personalizzata passati al piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Date and time of the latest successful synchronisation" -msgstr "Data e ora dell'ultima sincronizzazione riuscita" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Date and time of the next synchronisation" -msgstr "Data e ora della prossima sincronizzazione" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__days -msgid "Days" -msgstr "Giorni" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__path -msgid "Default Path" -msgstr "Percorso predefinito" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "Default full file name with file type for example: test.txt" -msgstr "Nome file completo predefinito con tipo di file, ad esempio: test.txt" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_delete_action -msgid "Delete from server" -msgstr "Cancella dal server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_dir -msgid "Directory on Server" -msgstr "Cartella nel server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__server_dir -msgid "Directory on server" -msgstr "Cartella nel server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__disconnect_file -msgid "Disconnect from Template" -msgstr "Disconneti dal modello" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__display_name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__display_name -msgid "Display Name" -msgstr "Nome visualizzato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__skip_host_key -msgid "Don't Check Key" -msgstr "Non controllare la chiave" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_download_action -msgid "Download" -msgstr "Scarica" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Due to security restrictions you are not allowed to delete %(fp)s" -msgstr "Per motivi di sicurezza non si è autorizzati a cancellare %(fp)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Duration" -msgstr "Durata" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "Duration, sec" -msgstr "Durata, sec" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_vault_vault_unique_key -msgid "Each secret (model, record, field) must be unique in the vault." -msgstr "Ogni segreto (modello, record, campo) deve essere univoco nell'archivio." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_dir -msgid "Eg '/home/user' or '/var/log'" -msgstr "Es '/home/user' o '/var/log'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__skip_host_key -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__skip_host_key -msgid "Enable to skip host key verification" -msgstr "Abilitare per saltare la verifica della chiave host" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Enter Python code here. Help about Python expression is available in the help tab of this document." -msgstr "Inserisci qui il codice Python. La guida sull'espressione Python è disponibile nella scheda della guida di questo documento." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_error -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Error" -msgstr "Errore" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Error retrieving host key: %(err)s" -msgstr "Errore nel recupero della chiave host: %(err)s" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "" -"Example:\n" -"
\n" -" \n" -" result = value.lower().strip().replace('_', '-').replace(\" \",\"\") if value.startswith('http') else 'https://' + re.sub(r'\\s+', '', value)\n" -" " -msgstr "" -"Esempio:\n" -"
\n" -" \n" -" result = value.lower().strip().replace('_', '-').replace(\" \",\"\") if value.startswith('http') else 'https://' + re.sub(r'\\s+', '', value)\n" -" " - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Execution Path" -msgstr "Percorso esecuzione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_status -msgid "Exit Code" -msgstr "Codice di uscita" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__e -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__e -msgid "Exit with command exit code" -msgstr "Uscita con comando codice di uscita" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__ec -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__ec -msgid "Exit with custom exit code" -msgstr "Uscita con codice di uscita personalizzato" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Failed" -msgstr "Fallito" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect after %(attempts)s attempts. Error: %(err)s" -msgstr "Connessione fallita dopo %(attempts)s tentativi. Errore: %(err)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Failed to connect. Error: %(err)s" -msgstr "Connessione fallita. Errore: %(err)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#, python-format -msgid "Failure" -msgstr "Fallimento" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__field_name -msgid "Field Name" -msgstr "Nome campo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__file_ids -msgid "File" -msgstr "File" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File %(f)s is not 'tower' type. This operation is supported for 'tower' files only" -msgstr "Il file %(f)s non è di tipo 'tower'. Questa operazione è supportata solo per file di tipo 'tower'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File %(f)s shouldn't have the '%(src)s' source for the '%(act)s' action" -msgstr "Il file %(f)s non dovrebbe avere l'origine '%(src)s' per l'azione '%(act)s'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__file_ids_count -msgid "File Count" -msgstr "Conteggio file" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_name -msgid "File Name" -msgstr "Nome file" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__file_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__file_template_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "File Template" -msgstr "Modello file" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__file_template_ids_count -msgid "File Template Count" -msgstr "Conteggio modello file" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__file_template_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "File Templates" -msgstr "Modelli file" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__file_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "File Type" -msgstr "Tipo file" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File already exists" -msgstr "Il file esiste già" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file_template.py:0 -#, python-format -msgid "File already exists on server." -msgstr "Il file esiste già nel server." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File already exists on server. Upload skipped" -msgstr "Il file esiste già nel server.Caricamento saltato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__code -msgid "File content" -msgstr "Contenuto del file" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "File content with variables rendered" -msgstr "Contenuto del file con variabili valorizzate" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File created and uploaded successfully" -msgstr "File creato e caricaco ocn successo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File deleted!" -msgstr "File cancellato!" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File downloaded!" -msgstr "File scaricato!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "File from template" -msgstr "File da modello" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__name -msgid "File name WITHOUT path. Eg 'test.txt'" -msgstr "Nome file senza percorso. Es. 'test.txt'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "File source cannot be determined: '%(source)s'" -msgstr "L'origine del file non può essere determinata: '%(source)s'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_id -msgid "File that will be executed to get the log data" -msgstr "File che verrà eseguito per ottenere i dati registro" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "File uploaded!" -msgstr "File caricato!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "File will be disconnected from template. Continue?" -msgstr "Il file verrà scollegato dal modello. Continuare?" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "File will be kept on server when deleted in Tower" -msgstr "Il file verrà mantenuto nel server quando cancellato in Tower" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__file_count -msgid "File(s)" -msgstr "File" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_action -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Files" -msgstr "File" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files deleted!" -msgstr "File cancellati!" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files downloaded!" -msgstr "File scaricati!" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "Files uploaded!" -msgstr "File caricati!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "" -"Files will be pulled from server to Tower\n" -" automatically using cron job.\n" -"
" -msgstr "" -"I file verranno trasferiti automaticamente dal\n" -" server a Tower tramite cron job.\n" -"
" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Finish date" -msgstr "Data fine" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__finish_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__finish_date -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Finished" -msgstr "Finito" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__plan_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__action__plan -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_shortcut__action__plan -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Flight Plan" -msgstr "Piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_run_line_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Flight Plan Lines" -msgstr "Riche piano di volo" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_plan_log -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan_log -msgid "Flight Plan Log" -msgstr "Registro piano di volo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Flight Plan Logs" -msgstr "Registri piano di volo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "Flight Plan Stopped" -msgstr "Piano di volo fermato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_used_ids -msgid "Flight Plan Used" -msgstr "Piani di volo utilizzati" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_used_ids_count -msgid "Flight Plan Used Ids Count" -msgstr "Conteggio ID usati piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Flight Plan line that is being currently executed" -msgstr "Riga piano di volo attualmente inesecuzione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Flight Plan will be terminated after executing the current command. Continue?" -msgstr "Il piano di volo verrà terminato dopo l'esecuzione di tale comando. Continuare?" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_plan -msgid "Flight Plans" -msgstr "Piani di volo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "Flight Plans Stopped" -msgstr "Piani di volo fermati" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Flight plan" -msgstr "Piano di volo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "Flight plan is not compatible with the server" -msgstr "Il piano di volo non è compatibile con il server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__flight_plan_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__plan_run_id -msgid "Flight plan run by the command" -msgstr "Piano di volo eseguito dal comando" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Flight plan running error" -msgstr "Errore esecuzione piano di volo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Flight plan running error %(err)s" -msgstr "Errore %(err)s esecuzione piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__flight_plan_used_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__flight_plan_used_ids_count -msgid "Flight plan this command is used in" -msgstr "Piano di volo questo comando viene utilizzato in" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__is_stopped -msgid "Flight plan was stopped by user" -msgstr "Il piano di volo è stato fermato dall'utente" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Float compare. Odoo helper function to compare floats." -msgstr "Confronto tra numeri in virgola mobile. Funzione di supporto Odoo per confrontare i numeri in virgola mobile." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_follower_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_follower_ids -msgid "Followers" -msgstr "Chi segue" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_partner_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_partner_ids -msgid "Followers (Partners)" -msgstr "Chi segue (partner)" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_type_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_type_icon -msgid "Font awesome icon e.g. fa-tasks" -msgstr "Font awesome icon es. fa-tasks" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_os__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__color -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_tag__color -msgid "For better visualization in views" -msgstr "Per una visualizzazione migliore nelle viste" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration_current -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration_current -msgid "For how long a flight plan is already running" -msgstr "Da quanto è in esecuzione un piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_run_wizard__applicability__this -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_run_wizard__applicability__this -msgid "For selected server(s)" -msgstr "Per server selezionati" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__full_server_path -msgid "Full Path" -msgstr "Percorso completo" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cetmix_tower_config_settings -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cetmix_tower_general_settings -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "General Settings" -msgstr "Impostazioni generali" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Get from Host" -msgstr "Ottieni da host" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__is_global -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__is_global -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Global" -msgstr "Globale" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Group By" -msgstr "Raggruppa per" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/common.py:0 -#, python-format -msgid "Group reference %s not found!" -msgstr "Riferimento gruppo %s non trovato!" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__has_message -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__has_message -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__has_message -msgid "Has Message" -msgstr "Ha un messaggio" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__has_missing_required_values -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__has_missing_required_values -msgid "Has Missing Required Values" -msgstr "Ha valori richiesti mancanti" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Has Template" -msgstr "Ha modello" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__have_access_to_server -msgid "Have Access To Server" -msgstr "Avere accesso al accesso" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Help" -msgstr "Aiuto" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard.py:0 -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__host_key -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__host_key -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__host_key -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -#, python-format -msgid "Host Key" -msgstr "Chiave host" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Host key is missing!" -msgstr "Manca la chiave host!" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Host key not found for server %(server)s" -msgstr "Chiave host non trovata per il server %(server)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__host_key -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__host_key -msgid "Host key to verify the server" -msgstr "Chiave host per verificare il server" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__hours -msgid "Hours" -msgstr "Ore" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -msgid "I confirm that the key is correct and I want to insert it in the server settings" -msgstr "Confermo che la chiave è corretta e voglio inserirla nelle impostazioni del server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__id -msgid "ID" -msgstr "ID" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_vault__res_id -msgid "ID of the resource that uses this vault" -msgstr "Id della risorsa che utilizza questo archivio" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v4_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v4_address -msgid "IPv4 Address" -msgstr "Indirizzo IPv4" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ip_v6_address -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ip_v6_address -msgid "IPv6 Address" -msgstr "Indirizzo IPv6" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon" -msgstr "Icona" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_icon -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "Icona per indicare una attività eccezione." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__if_file_exists -msgid "If File Exists" -msgstr "Se il file esiste già" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction -msgid "If checked, new messages require your attention." -msgstr "Se selezionata, nuovi messaggi richiedono attenzione." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "If checked, some messages have a delivery error." -msgstr "Se selezionata, alcuni messaggi hanno errori di consegna." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__auto_sync -msgid "If enabled file will be synced automatically using cron" -msgstr "Se abilitata, il file verrà sincronizzato automaticamente tramite cron" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__disconnect_file -msgid "If enabled, disconnects the file from its template after running the command.\n" -msgstr "Se abilitato, disconnette il file dal suo modello dopo aver eseguito il comando.\n" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__no_split_for_sudo -msgid "If enabled, do not split command on '&&' when using sudo.Prepend sudo once to the whole command." -msgstr "Se abilitata, non dividere il comando su '&&' quando si usa sudo. Anteporre sudo una volta all'intero comando." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__auto_sync -msgid "If enabled, files created from this template will have Auto Sync enabled by default. Used only with 'Tower' source." -msgstr "Se abilitata, i file creati da questo modello avranno Auto Sync abilitato in modo predefinito. Usato solo con l'origine 'Tower'." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__allow_parallel_run -msgid "" -"If enabled, multiple instances of the same command can be run on the same server at the same time.\n" -"Otherwise, ANOTHER_COMMAND_RUNNING status will be returned if another instance of the same command is already running" -msgstr "" -"Se abilitato, più istanze dello stesso comando possono essere eseguite contemporaneamente sullo stesso server. \n" -"In caso contrario, verrà restituito lo stato ANOTHER_COMMAND_RUNNING se un'altra istanza dello stesso comando è già in esecuzione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__allow_parallel_run -msgid "" -"If enabled, multiple instances of the same flight plan can be run on the same server at the same time.\n" -"Otherwise, ANOTHER_PLAN_RUNNING status will be returned if another instance of the same flight plan is already running" -msgstr "" -"Se abilitato, più istanze dello stesso piano di volo possono essere eseguite contemporaneamente sullo stesso server. \n" -"In caso contrario, verrà restituito lo stato ANOTHER_PLAN_RUNNING se un'altra istanza dello stesso piano di volo è già in esecuzione" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#, python-format -msgid "If exit code" -msgstr "Se codice di uscita" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/test_plan.py:0 -#, python-format -msgid "If exit code == 35 then Exit with command exit code" -msgstr "Se il codice di uscita == 35 allora esci con il comando codice di uscita" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "In the Secure Shell (SSH) protocol, host keys are used to verify the identity of remote hosts. Accepting unknown host keys may leave the connection open to man-in-the-middle attacks." -msgstr "Nel protocollo Secure Shell (SSH), le chiavi host vengono utilizzate per verificare l'identità degli host remoti. Accettare chiavi host sconosciute può esporre la connessione ad attacchi man-in-the-middle." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -msgid "Indicates if this variable is mandatory for server creation" -msgstr "Indica se questa variabile è obbligatoria per la creazione del server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_server_host_key_wizard_form -msgid "Insert Key" -msgstr "Inserire chiave" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__interval_number -msgid "Interval Number" -msgstr "Numero intervallo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__interval_type -msgid "Interval Unit" -msgstr "Unità intervallo" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_scheduled_task_interval_positive -msgid "Interval number must be greater than zero." -msgstr "Il numero intervallo deve essere maggiore di zero." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#, python-format -msgid "Invalid value!" -msgstr "Valore non valido!" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__is_error -msgid "Is Error" -msgstr "È errore" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_is_follower -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_is_follower -msgid "Is Follower" -msgstr "Segue" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_running -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__is_running -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__is_running -msgid "Is Running" -msgstr "In esecuzione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__is_skipped -msgid "Is Skipped" -msgstr "È saltato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__keep_when_deleted -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__keep_when_deleted -msgid "Keep When Deleted" -msgstr "Mantieni quando cancellato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__key_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__k -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__k -msgid "Key" -msgstr "Chiave" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__key_type -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Key Type" -msgstr "Tipo chiave" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Key Value" -msgstr "Valore chiave" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard.py:0 -#, python-format -msgid "Key inserted successfully!" -msgstr "Chiave inserita con successo!" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Key reference for inline usage" -msgstr "Riferimento chiave per utilizzo inline" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_key -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_key_root -msgid "Keys and Secrets" -msgstr "Chiavi e segreti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__label -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__label -msgid "Label" -msgstr "Etichetta" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Labeled" -msgstr "Etichettato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__last_call -msgid "Last Execution Date" -msgstr "Data ultima esecuzione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value____last_update -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault____last_update -msgid "Last Modified on" -msgstr "Ultima modifica il" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_last -msgid "Last Sync Date" -msgstr "Ultima data sincronia" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_uid -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__write_uid -msgid "Last Updated by" -msgstr "Ultimo aggiornamento di" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__write_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__write_date -msgid "Last Updated on" -msgstr "Ultimo aggiornamento il" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code_on_server -msgid "Latest version of file content on server" -msgstr "Ultima versione del contenuto del file sul server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__line_id -msgid "Line" -msgstr "Riga" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__flight_plan_line_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__line_ids -msgid "Lines" -msgstr "Righe" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__flight_plan_line_ids -msgid "Lines of the associated flight plan" -msgstr "Righe del piano di volo associato" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Local" -msgstr "Locale" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "Location where command will be executed. Overrides command default path. You can use {{ variables }} in path" -msgstr "Posizione in cui verrà eseguito il comando. Sostituisce il percorso predefinito del comando. Si può usare {{ variables }} nel percorso" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__path -msgid "Location where command will be run. You can use {{ variables }} in path" -msgstr "Posizione in cui verrà eseguito il comando. Puoi usare {{variabili}} nel percorso" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_text -msgid "Log Text" -msgstr "Testo registro" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__log_type -msgid "Log Type" -msgstr "Tipo registro" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_log_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -msgid "Logs" -msgstr "Registri" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_main_attachment_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_main_attachment_id -msgid "Main Attachment" -msgstr "Allegato principale" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__parent_flight_plan_log_id -msgid "Main Log" -msgstr "Registro principale" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plan" -msgstr "Piano principale" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Main plans" -msgstr "Piani principali" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_manager -msgid "Manager" -msgstr "Responsabile" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers" -msgstr "Responsabili" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__manager_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__manager_ids -msgid "Managers who can modify this record" -msgstr "Responsabili che possono modificare questo record" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error -msgid "Message Delivery error" -msgstr "Errore consegna messaggio" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__validation_message -msgid "" -"Message to display when the variable value is invalid. \n" -"First line will be added automatically: `Variable:, Value: `\n" -"Eg: `Variable: Customer Name, Value: Test\n" -"Invalid value!`\n" -"If empty, the default message will be used." -msgstr "" -"Messaggio da visualizzare quando il valore della variabile non è valido.\n" -"La prima riga verrà aggiunta automaticamente: `Variabile:, Valore: `\n" -"Es.: `Variabile: Nome cliente, Valore: Test\n" -"Valore non valido!`\n" -"Se vuoto, verrà utilizzato il messaggio predefinito." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_ids -msgid "Messages" -msgstr "Messaggi" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__minutes -msgid "Minutes" -msgstr "Minuti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables -msgid "Missing Required Variables" -msgstr "Variabili richieste mancanti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__missing_required_variables_message -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__missing_required_variables_message -msgid "Missing Required Variables Message" -msgstr "Messaggio variabili richieste mancanti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_vault__res_model -msgid "Model name of the resource that uses this vault" -msgstr "Nome modello della risorsa che utilizza questo archivio" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Modify Code" -msgstr "Modifica codice" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__months -msgid "Months" -msgstr "Mesi" - -#. module: cetmix_tower_server -#: model:cx.tower.variable,validation_message:cetmix_tower_server.variable_demo_branch -msgid "Must be lowercase and contain only letters and numbers!" -msgstr "Deve essere in minuscolo e contenere solo lettere e numeri!" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__my_activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "Scadenza mia attività" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__name -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__name -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Name" -msgstr "Nome" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_vault__field_name -msgid "Name of the field that contains the secret value" -msgstr "nome del campo che contiene il valore segreto" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_date_deadline -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "Scadenza attività successiva" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_summary -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_summary -msgid "Next Activity Summary" -msgstr "Riepilogo attività successiva" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_type_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_type_id -msgid "Next Activity Type" -msgstr "Tipo attività successiva" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__next_call -msgid "Next Execution Date" -msgstr "Data prossima esecuzione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__sync_date_next -msgid "Next Sync Date" -msgstr "Data sincronizzazione successiva" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__next_call -msgid "Next planned execution date for this task." -msgstr "Data prossima esecuzione pianificata per questo lavoro." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "No" -msgstr "No" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__no_split_for_sudo -msgid "No Split for sudo" -msgstr "Non dividere per sudo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Synced" -msgstr "Non sincronizzato" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "No Template" -msgstr "Nessun modello" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "" -"No output received. Please log in manually and check for any issues.\n" -"===\n" -"CODE: %(status)s" -msgstr "" -"Nessun output ricevuto. Accedere manualmente e verifica eventuali problemi.\n" -"===\n" -"CODE: %(status)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "No runner found for command action '%(cmd_action)s'" -msgstr "Nessuna esecuzione trovata per l'azione comando '%(cmd_action)s'" - -#. module: cetmix_tower_server -#. odoo-javascript -#: code:addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml:0 -#, python-format -msgid "No secrets found" -msgstr "Segreto non trovato" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "No server found for the provided reference." -msgstr "Nessun server trovato per il riferimento fornito." - -#. module: cetmix_tower_server -#. odoo-javascript -#: code:addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml:0 -#, python-format -msgid "No variables found" -msgstr "Variabili non trovate" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "No/Undefined" -msgstr "No/indefinito" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_run_wizard__applicability__shared -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_run_wizard__applicability__shared -msgid "Non server restricted" -msgstr "Non limitato al server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Not Running" -msgstr "Non in esecuzione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__note -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__note -msgid "Note" -msgstr "Note" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of Actions" -msgstr "Numero di azioni" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of errors" -msgstr "Numero di errori" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_needaction_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "Numero di messaggi che richiedono una azione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__message_has_error_counter -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__message_has_error_counter -msgid "Number of messages with delivery error" -msgstr "Numero di messaggi con errore di consegna" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_os -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "OS" -msgstr "SO" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "OS %(os)s used by the server '%(srv)s' is not present in the command's OS compatibility list" -msgstr "Il sistema operativo %(os)s utilizzato dal server '%(srv)s' non è presente nell'elenco di compatibilità del sistema operativo del comando" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__os_ids -msgid "OSes" -msgstr "SO" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Odoo Environment" -msgstr "Ambiente Odoo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_delete_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "On Delete Plan" -msgstr "Su cancella piano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "On Error" -msgstr "Su errore" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Only one global secret value can be defined for a key" -msgstr "È possibile definire un solo valore segreto globale per una chiave" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Only one global value can be defined for variable '%(var)s'" -msgstr "Per la variabile '%(var)s' può essere definito un solo valore globale" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/test_variable.py:0 -#, python-format -msgid "Only one global value can be defined for variable 'meme'" -msgstr "Per la variabile 'meme' può essere definito un solo valore globale" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Only one secret value can be defined for a partner" -msgstr "È possibile definire un solo valore segreto globale per una chiave" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Only one secret value can be defined for a server" -msgstr "È possibile definire un solo valore segreto globale per una chiave" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_key_value.py:0 -#, python-format -msgid "Only one secret value can be defined for a server and partner" -msgstr "È possibile definire un solo valore segreto per un server e un partner" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Only values that the current user has access to are shown." -msgstr "Vengono visualizzati solo i valori a cui l'utente corrente ha accesso." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open" -msgstr "Apri" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Open full form" -msgstr "Apri maschera completa" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__os_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__os_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__os_id -msgid "Operating System" -msgstr "Sistema operativo" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_os -msgid "Operating Systems" -msgstr "Sistemi operativi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__option_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__option_id -msgid "Option" -msgstr "Opzione" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Option '%(val)s' is not available for variable '%(var)s'" -msgstr "L'opzione '%(val)s' non è disponibile per la variabile '%(var)s'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__option_ids -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__o -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Options" -msgstr "Opzioni" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__os_compatibility_warning -msgid "Os Compatibility Warning" -msgstr "Avviso di compatibilità del sistema operativo" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command__if_file_exists__overwrite -msgid "Overwrite" -msgstr "Sovrascrivi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__partner_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__partner_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__partner_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Partner" -msgstr "Partner" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key_value__partner_id -msgid "Partner to which the key belongs" -msgstr "Partner a cui appartiene la chiave" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_cetmix_tower_partner -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_partner -msgid "Partners" -msgstr "Partner" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__ssh_auth_mode__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__ssh_auth_mode__p -msgid "Password" -msgstr "Password" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__path -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__path -msgid "Path" -msgstr "Percorso" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__plan_domain -msgid "Plan Domain" -msgstr "Dominio piano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__plan_line_ids -msgid "Plan Line" -msgstr "Riga piano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__plan_line_action_id -msgid "Plan Line Action" -msgstr "Azione riga piano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__plan_line_ids_count -msgid "Plan Line Count" -msgstr "Conteggio riga piano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_line_executed_id -msgid "Plan Line Executed" -msgstr "Riga piano eseguita" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -#, python-format -msgid "Plan Lines" -msgstr "Righe piano" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard.py:0 -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__plan_log_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_log_ids -#, python-format -msgid "Plan Log" -msgstr "Registro piano" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__is_running -msgid "Plan is being executed right now" -msgstr "Il piano è appena stato eseguito" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Plan line condition check failed." -msgstr "Controllo condizione riga piano fallito." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__plan_ids -msgid "Plans" -msgstr "Piani" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide IPv4 or IPv6 address for %(srv)s" -msgstr "Fornire un indirizzo un indirizzo IPv4 o IPv6 per %(srv)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH Key for %(srv)s" -msgstr "Fornire la chiave SSH per %(srv)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Please provide SSH password for %(srv)s" -msgstr "Fornire la password SSH per %(srv)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py:0 -#, python-format -msgid "Please provide values for the following configuration variables: %(variables)s" -msgstr "Fornire valori per le seguenti variabili configurabili: %(variables)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Please provide values for the following configuration variables: %(vars)s" -msgstr "Fornire valori per le seguenti variabili configurabili: %(vars)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "Please resolve the following issues with configuration variables:" -msgstr "Risolvere il seguente problema con le variabili configurazione:" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Please select a command to execute" -msgstr "Selezionare un comando da eseguire" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Post Run Actions" -msgstr "Azioni post esecuzione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Preview" -msgstr "Anteprima" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__parent_id -msgid "Previous Version" -msgstr "Versione anteprima" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__last_call -msgid "Previous time the task ran successfully." -msgstr "Ora precedente in cui il lavoro è stato eseguito con successo." - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_property_root -msgid "Properties" -msgstr "Proprietà" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "Pull files from server" -msgstr "Ricevi file dal server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Pull from Server" -msgstr "Ricevi dal server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Push to Server" -msgstr "Invia al server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__path -msgid "" -"Put custom path to run the command.\n" -"IMPORTANT: this field does NOT support variables!" -msgstr "" -"Inserire il percorso personalizzato per eseguire il comando.\n" -"IMPORTANTE: questo campo NON supporta variabili!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_view_form -msgid "Put your notes here" -msgstr "Inserire qui le proprie note" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Put your notes here..." -msgstr "Inserire qui le proprie note..." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'datetime' library" -msgstr "Libreria Python 'datetime'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'dateutil' library" -msgstr "Libreria Python 'dateutil'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'dnspython' library. Documentation.
  • dns.resolver: wrapped dnspython. Use dns.resolver.resolve(hostname, \"A\") for DNS lookups.
  • dns.reversename: wrapped dnspython. Use dns.reversename.from_address(\"8.8.8.8\") to build and reverse PTR records.
  • dns.exception: wrapped dnspython. Catch dns.exception.DNSException to handle DNS-related errors.
" -msgstr "Libreria Python 'dnspython'. Documentazione.
  • dns.resolver: dnspython incapsulato. Utilizza dns.resolver.resolve(hostname, \"A\") per le ricerche DNS.
  • dns.reversename: dnspython incapsulato. Utilizza dns.reversename.from_address(\"8.8.8.8\") per creare e invertire i record PTR.
  • dns.exception: dnspython incapsulato. Cattura dns.exception.DNSException per gestire gli errori relativi al DNS.
" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'hashlib' library. Documentation. Available methods: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', 'shake_128', 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'" -msgstr "Libreria Python 'hashlib'. Documentazione. Metodi disponibili: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', 'shake_128', 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'hmac' library. Documentation. Use 'new' to create HMAC objects. Available methods on the HMAC *object*: 'update', 'copy', 'digest', 'hexdigest'. Module-level function: 'compare_digest'." -msgstr "Libreria Python 'hmac'. Documentazione. Usa 'new' per creare oggetti HMAC. Metodi disponibili sull'oggetto HMAC: 'update', 'copy', 'digest', 'hexdigest'. Funzione a livello di modulo: 'compare_digest'." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'json' library. Available methods: 'dumps'" -msgstr "Libreria Python 'json'. Metodi disponibili: 'dumps'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'requests' library. Available methods: 'post', 'get', 'delete', 'request'" -msgstr "Libreria Python \"requests\". Metodi disponibili: \"post\", \"get\", \"delete\", \"request\"" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'time' library" -msgstr "Libreria Python 'time'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'timezone' library" -msgstr "Libreria Python 'timezone'" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "Python 'tldextract' library. Use tldextract.extract() to parse domains. Check tldextract for more information." -msgstr "Libreria Python 'tldextract'. Utilizza tldextract.extract() per analizzare i domini. Consulta tldextract per ulteriori informazioni." - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_run_wizard__action__python_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Python code" -msgstr "Codice Python" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "Python code running error: %(err)s" -msgstr "Errore esecuzione codice Python: %(err)s" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "" -"Python code that is used to modify the variable values.\n" -"
\n" -" Available variables and functions:" -msgstr "" -"Codice Python utilizzato per modificare i valori delle variabili.\n" -"
\n" -" Variabili e funzioni disponibili:" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__applied_expression -msgid "" -"Python expression to apply to the variable value. \n" -"You can use general python sting functions and 're' module for regex operations. Use 'value' variable to refer to the variable value, use 'result' to assign the final result that will be used as a variable value.\n" -"Eg 'result = value.lower().replace(' ', '_')'" -msgstr "" -"Espressione Python da applicare al valore della variabile.\n" -"È possibile utilizzare le funzioni stinghe generali di Python e il modulo 're' per le operazioni regex. Utilizzare la variabile 'value' per fare riferimento al valore della variabile, utilizzare 'result' per assegnare il risultato finale che verrà utilizzato come valore della variabile.\n" -"Es. 'result = value.lower().replace(' ', '_')'" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command__if_file_exists__raise -msgid "Raise Error" -msgstr "Attiva errore" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0 -#, python-format -msgid "Recursive plan call detected in plan %(name)s." -msgstr "Rilevata chiamata piano ricorsiva nel piano %(name)s." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_os__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_reference_mixin__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__reference -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__reference -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Reference" -msgstr "Riferimento" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__reference_code -msgid "Reference Code" -msgstr "Codice riferimento" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_command_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_file_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_file_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_project_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_project_rel_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_remote_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_repo_owner_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_repo_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_git_source_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_key_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_key_value_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_os_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_action_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_line_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_plan_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_reference_mixin_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_scheduled_task_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_log_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_server_template_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_shortcut_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_tag_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_webhook_authenticator_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_webhook_eval_mixin_reference_unique -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_webhook_reference_unique -msgid "Reference must be unique" -msgstr "Il riferimento deve essere univoco" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Reference. Can contain English letters, digits and '_'. Leave blank to autogenerate" -msgstr "Riferimento. Può contenere lettere inglesi, cifre e '_'. Lasciare vuoto per la generazione automatica" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_log_view_form -msgid "Refresh" -msgstr "Aggiorna" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Refresh All" -msgstr "Aggiorna tutto" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Regex expression, eg. ^[a-z0-9]+$" -msgstr "Espressione regex, eg. ^[a-z0-9]+$" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable__validation_pattern -msgid "" -"Regex pattern to validate the variable values using the 're.match' function. Eg. ^[a-z0-9]+$ \n" -"If empty, the variable values will not be validated." -msgstr "" -"Modello regex per convalidare i valori delle variabili utilizzando la funzione 're.match'. Ad esempio ^[a-z0-9]+$\n" -"Se vuoto, i valori delle variabili non verranno convalidati." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "" -"Remember: Python code is executed on the Tower server, not on the remote\n" -" one." -msgstr "Ricorda: il codice Python viene eseguito sul server Tower, non su quello remoto." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Remember: Python code is executed on the Tower server, not on the remote one." -msgstr "Ricorda: il codice Python viene eseguito sul server Tower, non su quello remoto." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__rendered_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_code -msgid "Rendered Code" -msgstr "Codice realizzato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_name -msgid "Rendered Name" -msgstr "Nome realizzato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__rendered_server_dir -msgid "Rendered Server Dir" -msgstr "Cartella server realizzato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__interval_number -msgid "Repeat every x." -msgstr "Ripeti ogni x." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__required -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__required -msgid "Required" -msgstr "Richiesto" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__res_id -msgid "Resource ID" -msgstr "ID risorsa" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__res_model -msgid "Resource Model" -msgstr "Modello risorsa" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__command_response -msgid "Response" -msgstr "Risposta" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__activity_user_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__activity_user_id -msgid "Responsible User" -msgstr "Utente responsabile" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__result -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Result" -msgstr "Risultato" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_root -msgid "Root" -msgstr "Radice" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "Run" -msgstr "Esegui" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"Run\n" -" Command" -msgstr "" -"Esegui\n" -" comando" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban -msgid "" -"Run\n" -" Flight Plan" -msgstr "" -"Esegui\n" -" piano di volo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#, python-format -msgid "Run Command" -msgstr "Esegui comando" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_command_run_wizard -msgid "Run Command in Wizard" -msgstr "Eseguire comando nella procedura guidata" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model:ir.actions.server,name:cetmix_tower_server.action_execute_cx_tower_plan -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__plan_run_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#, python-format -msgid "Run Flight Plan" -msgstr "Esegui piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_plan_run_wizard -msgid "Run Flight Plan in Wizard" -msgstr "Esegui piano di volo nella procedura guidata" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Run Manually" -msgstr "Esegui manualmente" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Run New Command" -msgstr "Esegui nuovo comando" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "Run Plan" -msgstr "Esegui piano" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Run Result" -msgstr "Eseguire risultato" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Run a flight plan" -msgstr "Esegui un piano di volo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Run code as it appears in 'Rendered code' in wizard and return to wizard. Result will not be logged" -msgstr "Eseguire il codice come appare in 'Codice realizzato' nella procedura guidata e torna alla procedura guidata. Il risultato non verrà registrato" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Run code using server method and log result" -msgstr "Eseguire il codice utilizzando il metodo server e registra il risultato" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Run command" -msgstr "Esegui comando" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_shortcut__use_sudo -msgid "Run command using 'sudo'" -msgstr "Esegui comando usando 'sudo'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__use_sudo -msgid "Run commands using 'sudo'" -msgstr "Esegui comando usando 'sudo'" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__use_sudo -msgid "Run commands using 'sudo'. Leave empty if 'sudo' is not needed." -msgstr "Eseguire i comandi usando 'sudo'. Lasciare vuoto se 'sudo' non è necessario." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Run in wizard" -msgstr "Esegui procedura guidata" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan__on_error_action__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_plan_line_action__action__n -msgid "Run next command" -msgstr "Esegui comando successivo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "Run on" -msgstr "Esegui su" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "Run scheduled tasks" -msgstr "Esegui lavori schedulati" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "Running" -msgstr "In esecuzione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Running Now" -msgstr "In esecuzione ora" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_auth_mode -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_auth_mode -msgid "SSH Auth Mode" -msgstr "Metodo autorizzazione SSH" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH Client is not defined." -msgstr "Il client SSH non è definito." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "SSH Command" -msgstr "Comando SSH" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__k -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "SSH Key" -msgstr "Chiave SSH" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_key -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "SSH Key / Secret" -msgstr "Chiave / segreto SSH" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_password -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_password -msgid "SSH Password" -msgstr "Password SSH" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__secret_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_key_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_key_id -msgid "SSH Private Key" -msgstr "Chiave privata SSH" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_username -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "SSH Username" -msgstr "Nome utente SSH" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_run_wizard__action__ssh_command -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "SSH command" -msgstr "Comando SSH" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH connection error %(err)s" -msgstr "Errore connessione SSH %(err)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__ssh_port -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_port -msgid "SSH port" -msgstr "Porta SSH" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "SSH run command error %(err)s" -msgstr "Errore esecuzione comando SSH %(err)s" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_scheduled_task -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__scheduled_task_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__scheduled_task_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__scheduled_task_id -msgid "Scheduled Task" -msgstr "Lavoro schedulato" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_scheduled_task -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__scheduled_task_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__scheduled_task_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_scheduled_task -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Scheduled Tasks" -msgstr "Lavori schedulati" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__scheduled_task_id -msgid "Scheduled task that triggered this command" -msgstr "Lavoro schedulato che ha eseguito questo comando" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__scheduled_task_id -msgid "Scheduled task that triggered this flight plan" -msgstr "Lavoro schedulato che ha eseguito questo piano di volo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#, python-format -msgid "Scheduled tasks run successfully." -msgstr "Lavori schedulati eseguito con successo." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.res_config_settings_view_form -msgid "" -"Scheduled tasks will be run automatically using cron job.\n" -"
" -msgstr "" -"I lavori schedulati verranno eseguiti automaticamente usando un lavoro cron.\n" -"
" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -msgid "Search Command Log" -msgstr "Ricerca registro comando" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -msgid "Search Commands" -msgstr "Ricerca comandi" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -msgid "Search File Templates" -msgstr "Ricerca modelli file" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Search Files" -msgstr "Ricerca file" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Search Flight Plan Log" -msgstr "Ricerca registro piano di volo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Search Flight Plans" -msgstr "Ricerca piani di volo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Search Keys/Secrets" -msgstr "Ricerca kiavi/segreti" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_os_search_view -msgid "Search OS" -msgstr "Ricerca SO" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_scheduled_task_search_view -msgid "Search Scheduled Tasks" -msgstr "Ricerca lavori schedulati" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_search_view -msgid "Search Server Templates" -msgstr "Ricerca modelli server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Search Servers" -msgstr "Ricerca server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Search Shortcuts" -msgstr "Ricerca scrociatoie" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_tag_search_view -msgid "Search Tags" -msgstr "Ricerca etichette" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Search Values" -msgstr "Ricerca valori" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_key__key_type__s -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_search_view -msgid "Secret" -msgstr "Segreto" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_vault__data -msgid "Secret Data" -msgstr "Dato segreto" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__secret_value -msgid "Secret Value" -msgstr "Valore segreto" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Secret Values" -msgstr "Valori segreti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_mixin__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_partner__secret_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_users__secret_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_res_partner_form_inherit_cetmix_tower -msgid "Secrets" -msgstr "Segreti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__applicability -msgid "" -"Selected server(s): only Commands that are specific to the selected server(s)\n" -"Non server restricted: all Commands that are not specific to any server" -msgstr "" -"Server selezionati: solo comandi specifici per il/i server selezionato/i\n" -"Non limitato al server: tutti i comandi che non sono specifici per alcun server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_run_wizard__applicability -msgid "" -"Selected server(s): only Flight Plans that are specific to the selected server(s)\n" -"Non server restricted: all Flight Plans that are not specific to any server" -msgstr "" -"Server selezionati: solo i piani di volo specifici per il/i server selezionato/i\n" -"Non limitato al server: tutti i piani di volo non specifici per alcun server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__sequence -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__sequence -msgid "Sequence" -msgstr "Sequenza" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key_value__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_host_key_wizard__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_id -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Server" -msgstr "Server" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_ir_actions_server -msgid "Server Action" -msgstr "Azione server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_partner__server_count -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_users__server_count -msgid "Server Count" -msgstr "Conteggio server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__server_log_ids -msgid "Server Log" -msgstr "Registro server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_log_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server Logs" -msgstr "Registri server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__name -msgid "Server Name" -msgstr "Nome server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__server_response -msgid "Server Response" -msgstr "Risposta server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_status -msgid "Server Status" -msgstr "Stato server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__server_template_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__server_template_id -msgid "Server Template" -msgstr "Modello server" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server_template -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__server_template_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__server_template_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__server_template_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_view_form -msgid "Server Templates" -msgstr "Modelli server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Server URL, eg 'https://meme.example.com'" -msgstr "URL server, es. 'https://meme.example.com'" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_form -msgid "Server Version" -msgstr "Versione server" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Server not found" -msgstr "Server non trovato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__server_response -msgid "" -"Server response received during the last operation.\n" -"Default value if no error happened is 'ok'.\n" -"Otherwise there will be a server error message logged." -msgstr "" -"Risposta del server ricevuta durante l'ultima operazione.\n" -"Il valore predefinito se non si è verificato alcun errore è 'ok'.\n" -"In caso contrario, verrà registrato un messaggio di errore del server." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Server tight" -msgstr "Server ridotto" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key_value__server_id -msgid "Server to which the key belongs" -msgstr "Server a cui appartiene le chiave" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__url -msgid "Server web interface, eg 'https://doge.example.com'" -msgstr "Interfaccia server web, es. 'https://doge.example.com'" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_partner__server_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_res_users__server_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_root -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_res_partner_form_inherit_cetmix_tower -msgid "Servers" -msgstr "Server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_ids -msgid "" -"Servers on which the command will be run.\n" -"If empty, command can be run on all servers" -msgstr "" -"Server in cui il comando verrà eseguito.\n" -"Se vuoto, il comando può essere eseguito su tutti i server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -msgid "Set Custom Values" -msgstr "Imposta valori personalizzati" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Set Variable Values" -msgstr "Imposta valori veriabile" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__server_status -msgid "Set the following status if command finishes with success. Leave 'Undefined' if you don't need to update the status" -msgstr "Impostare lo stato seguente se il comando è eseguito con successo. Lasciare 'Non definito' se non serve aggiornare lo stato" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_settings -msgid "Settings" -msgstr "Impostazioni" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_shortcut_search_view -msgid "Shortcut" -msgstr "Scorciatoia" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_shortcut -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__shortcut_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__shortcut_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_shortcut -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Shortcuts" -msgstr "Scorciatoie" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "Show Commands" -msgstr "Mostra comandi" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "Show Flight Plans" -msgstr "Mostra piani di volo" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_server_host_key_wizard -msgid "Show Host Key" -msgstr "Mmostra chiave host" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__show_servers -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__show_servers -msgid "Show Servers" -msgstr "Visualizza server" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command__if_file_exists__skip -msgid "Skip" -msgstr "Salta" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__skip_host_key -msgid "Skip Host Key" -msgstr "Salta chiave host" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Skipped" -msgstr "Saltato" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "Some servers don't support this command" -msgstr "Alcuni server non supportano questo comando" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "" -"Some variable options are invalid:\n" -"%(detailed_message)s" -msgstr "" -"Alcune opzioni variabile non sono valide:\n" -"%(detailed_message)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__source -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__source -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Source" -msgstr "Origine" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -msgid "Start date" -msgstr "Data partenza" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__start_date -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__start_date -msgid "Started" -msgstr "Partito" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__plan_status -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__status -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view -msgid "Status" -msgstr "Stato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_state -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_state -msgid "" -"Status based on activities\n" -"Overdue: Due date is already passed\n" -"Today: Activity date is today\n" -"Planned: Future activities." -msgstr "" -"Stato basato sulle attività\n" -"Scaduto: la data di scadenza è già passata\n" -"Oggi: la data dell'attività è oggi\n" -"Pianificato: attività future." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Stop" -msgstr "Ferma" - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.action_stop_cx_tower_plan_log -msgid "Stop Flight Plan" -msgstr "Ferma piano di volo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__is_stopped -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_form -msgid "Stopped" -msgstr "Fermato" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command_log.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0 -#, python-format -msgid "Stopped by user %(user)s" -msgstr "Fermato dall'utente %(user)s" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_variable__variable_type__s -msgid "String" -msgstr "Stringa" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_search_view -#, python-format -msgid "Success" -msgstr "Successo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Sync Error" -msgstr "Errore sincronizzazione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Synced" -msgstr "Sincronizzato" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_tag -msgid "Tag" -msgstr "Etichetta" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_search_view -msgid "Tagged" -msgstr "Etichettato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__tag_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_tag_mixin__tag_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_tag -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Tags" -msgstr "Etichette" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__template_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Template" -msgstr "Modello" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__template_code -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Template Code" -msgstr "Codice modello" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_file_template_action -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_file_template -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_template -msgid "Templates" -msgstr "Modelli" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Test Connection" -msgstr "Test connessione" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_search -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Text" -msgstr "Testo" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0 -#, python-format -msgid "" -"The access level for Variable Option '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Option Access Level: %(val_level)s" -msgstr "" -"Il livello di accesso per l'opzione variabile '%(value)s' non può essere inferiore al livello di accesso della sua variabile '%(variable)s'.\n" -"Livello di accesso variabile: %(var_level)s\n" -"Livello di accesso opzione variabile: %(val_level)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "" -"The access level for Variable Value '%(value)s' cannot be lower than the access level of its Variable '%(variable)s'.\n" -"Variable Access Level: %(var_level)s\n" -"Variable Value Access Level: %(val_level)s" -msgstr "" -"Il livello di accesso per il valore variabile '%(value)s' non può essere inferiore al livello di accesso della sua variabile '%(variable)s'.\n" -"Livello di accesso variabile: %(var_level)s\n" -"Livello di accesso valore variabile: %(val_level)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0 -#, python-format -msgid "The access level of command(s) '%(command_names)s' included in the current Flight plan is higher than the access level of the Flight plan itself. Please ensure that you want to allow those commands to be run anyway." -msgstr "Il livello di accesso dei comandi '%(command_names)s' inclusi nel piano di volo corrente è superiore al livello di accesso del piano di volo stesso. Assicurati di voler davvero consentire a tutti quei comandi di essere eseguiti comunque." - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_unique_variable_option_name -msgid "The combination of Name and Variable must be unique." -msgstr "La combinazione di Nome e Variabile deve essere univoca." - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_option_unique_variable_option -msgid "The combination of Value and Variable must be unique." -msgstr "La combinazione di Valore e Variabile deve essere univoca." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0 -#, python-format -msgid "The file %(f_path)s not found." -msgstr "File %(f_path)s non trovato." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_vault__data -msgid "The secret data to be stored in the vault" -msgstr "Il dato segreto da salmare nell'archivio" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#, python-format -msgid "The selected task interval is too low in relation to the general system settings. This may lead to task execution delays." -msgstr "L'intervallo selezionato del lavoro è troppo piccolo in relazione alle impostazioni generali di sistema. Questo può portare a ritardi di esecuzione del lavoro." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_action_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "Then" -msgstr "Quindi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__plan_delete_id -msgid "This Flightplan will be executed when the server is deleted" -msgstr "Questo piano di volo verrà eseguito quando il server viene cancellato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__plan_delete_id -msgid "This Flightplan will be run when the server is deleted" -msgstr "La combinazione di Valore e Variabile deve essere univoca" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__on_error_action -msgid "This action will be triggered on error if no command action can be applied" -msgstr "Questa azione verrà attivata in caso di errore se non è possibile applicare alcuna azione di comando" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "This command can be used only in Flight Plans." -msgstr "Questo comando può essere usato solo nei piani di volo." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__note -msgid "This field is used to put some notes regarding template." -msgstr "Questo file è usato per inserire alcune note relative al modello." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__command_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_template_mixin__code -msgid "This field will be rendered using variables" -msgstr "Questo campo verrà renderizzato utilizzando le variabili" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__file_template_id -msgid "This file template will be used to create log files when server is created from a template" -msgstr "Questo modello di file verrà utilizzato per creare file di registro quando il server viene creato da un modello" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__flight_plan_id -msgid "This flight plan will be run upon server creation" -msgstr "Questo piano di volo verrà eseguito durante la creazione del server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template_create_wizard__ssh_username -msgid "This is required, however you can change this later in the server settings" -msgstr "Questo è richiesto, comunque lo si può modificare dopo nelle impostazioni del server" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__file_template_id -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__file_template_id -msgid "This template will be used to create or update the pushed file" -msgstr "Questo modello verrà utilizzato per creare o aggiornare il file inviato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key_value__is_global -msgid "This value is applicable to all servers and partners" -msgstr "Questo campo verrà reso utilizzato utilizzando le variabili" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__duration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__duration -msgid "Time consumed for execution, seconds" -msgstr "Tempo utilizzato per l'esecuzione, secondi" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_res_config_settings__cetmix_tower_command_timeout -msgid "Timeout for commands in seconds after which the command will be terminated" -msgstr "Questo campo verrà reso utilizzando le variabili" - -#. module: cetmix_tower_server -#: model:ir.ui.menu,name:cetmix_tower_server.menu_tools -msgid "Tools" -msgstr "Strumenti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__file_count -msgid "Total Files" -msgstr "File totali" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file__source__tower -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_file_template__source__tower -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_view_search -msgid "Tower" -msgstr "Tower" - -#. module: cetmix_tower_server -#: model:ir.model,name:cetmix_tower_server.model_cx_tower_variable_mixin -msgid "Tower Variables mixin" -msgstr "Mixin variabili Tower" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "Trigger shortcut" -msgstr "Attiva scorciatoia" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form -msgid "Triggered Commands" -msgstr "Comando attivati" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__triggered_plan_command_log_ids -msgid "Triggered Flight Plan Commands" -msgstr "Comandi del piano di volo attivati" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__triggered_plan_log_id -msgid "Triggered Plan Log" -msgstr "Registro piano attivato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_type -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_type -msgid "Type" -msgstr "Tipo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__activity_exception_decoration -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__activity_exception_decoration -msgid "Type of the exception activity on record." -msgstr "Tipo attività eccezione sul record." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__url -msgid "URL" -msgstr "URL" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to delete file '%(f)s'.\n" -"Delete operation is not supported for 'server' type files." -msgstr "" -"Impossibile eliminare il file '%(f)s'. \n" -"L'operazione di eliminazione non è supportata per i file di tipo 'server'." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0 -#, python-format -msgid "Unable to run scheduled task '%(f)s'. Error: %(e)s" -msgstr "Impossibile eseguire l'attività pianificata '%(f)s'. Errore: %(e)s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0 -#, python-format -msgid "" -"Unable to upload file '%(f)s'.\n" -"Upload operation is not supported for 'server' type files." -msgstr "" -"Impossibile caricare il file '%(f)s'. \n" -"L'operazione di caricamento non è supportata per i file di tipo 'server'." - -#. module: cetmix_tower_server -#: model:ir.actions.server,name:cetmix_tower_server.cetmix_tower_file_upload_action -msgid "Upload" -msgstr "Carica" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_shortcut__use_sudo -msgid "Use Sudo" -msgstr "Utilizzare sudo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__use_sudo -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard__use_sudo -msgid "Use sudo" -msgstr "Utilizzare sudo" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH Key" -msgstr "Utilizzare una chiave SSH" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__server_ssh_ids -msgid "Used as SSH key in the following servers" -msgstr "Utilizzata una chiave SSH nei seguenti server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -msgid "Used for" -msgstr "Utilizzato per" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_search_view -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "Used in Plans" -msgstr "Usato nei piani" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Used in Values" -msgstr "Usato nei valori" - -#. module: cetmix_tower_server -#: model:res.groups,name:cetmix_tower_server.group_user -msgid "User" -msgstr "Utente" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0 -#, python-format -msgid "UserError. Helper to raise UserError." -msgstr "UserError. Helper per generare UserError." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users" -msgstr "Utenti" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_access_role_mixin__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file_template__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_key__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_scheduled_task__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__user_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_template__user_ids -msgid "Users who can view this record" -msgstr "Utenti che possono vedere questo record" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__validation_message -msgid "Validation Message" -msgstr "Messaggi di validazione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__validation_pattern -msgid "Validation Pattern" -msgstr "Schema di validazione" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__value_char -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__value_char -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Value" -msgstr "Valore" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids_count -msgid "Value Count" -msgstr "Conteggio valori" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Value Modifier" -msgstr "Modificatore valore" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#: code:addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py:0 -#, python-format -msgid "Value is invalid" -msgstr "Il valore è valido" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_key__value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__value_ids -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "Values" -msgstr "Valori" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_option__variable_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_id -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_value_search_view -msgid "Variable" -msgstr "Variabile" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0 -#, python-format -msgid "Variable '%(var)s' can only be assigned to one of the models at a time: Server, Server Template, or Plan Line Action." -msgstr "La variabile '%(var)s' può essere assegnata solo ad un modello alla volta: server, modello server, o azione riga piano." - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_reference -msgid "Variable Reference" -msgstr "Riferimento variabile" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_custom_variable_value_mixin__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task_cv__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__variable_value_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_value_ids -msgid "Variable Value" -msgstr "Valore variabile" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__variable_value_ids_count -msgid "Variable Value Count" -msgstr "Conteggio valori variabile" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable_value -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__variable_values -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__variable_values -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template__variable_value_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable_value -#, python-format -msgid "Variable Values" -msgstr "Valori variabile" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_tower_variable_value_uniq -msgid "Variable can be declared only once for the same record!" -msgstr "La variabile può essere dichiarata solo una volta per lo stesso record!" - -#. module: cetmix_tower_server -#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_name_uniq -msgid "Variable names must be unique" -msgstr "Il nome variabile deve essere univoco" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable not found" -msgstr "Variabile non trovato" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0 -#, python-format -msgid "Variable reference '%(var_ref)s' has an invalid option reference '%(opt_ref)s'." -msgstr "Il riferimento variabile '%(var_ref)s' ha un riferimento opzione non valido '%(opt_ref)s'." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_template_mixin.py:0 -#, python-format -msgid "Variable syntax error: %s" -msgstr "Errore sintassi variabile: %s" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value created" -msgstr "Valore variabile creato" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0 -#, python-format -msgid "Variable value updated" -msgstr "Valore variabile aggiornato" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__variable_value_ids -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_variable_mixin__variable_value_ids -msgid "Variable values for selected record" -msgstr "Valori variabile per il record selezionato" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0 -#, python-format -msgid "" -"Variable: %(var)s, Value: %(val)s\n" -"%(msg)s" -msgstr "" -"Variabile: %(var)s, Valore: %(val)s\n" -"%(msg)s" - -#. module: cetmix_tower_server -#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_variable -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file_template__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_template_mixin__variable_ids -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__variable_ids -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable -#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_variable_root -msgid "Variables" -msgstr "Variabili" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__warning_message -msgid "Warning Message" -msgstr "Messaggio di avviso" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__os_compatibility_warning -msgid "Warning about OS compatibility of the command" -msgstr "Avviso sulla compatibilità del comando con il sistema operativo" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__weeks -msgid "Weeks" -msgstr "Settimane" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__if_file_exists -msgid "" -"What to do if file already exists on the server.\n" -"- Skip: Do not create or update the file.\n" -"- Overwrite: Replace the existing file with the new one.\n" -"- Raise Error: Raise an error if the file already exists." -msgstr "Cosa fare se il file esiste già sul server.- Salta: non creare o aggiornare il file.- Sovrascrivi: sostituisci il file esistente con quello nuovo.- Genera errore: genera un errore se il file esiste già." - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__path -msgid "Where command was executed" -msgstr "Dove è stato eseguito il comando" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan__custom_exit_code -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line_action__custom_exit_code -msgid "Will be used instead of the command exit code" -msgstr "Può essere usato al posto del comando codice uscita" - -#. module: cetmix_tower_server -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_line__use_sudo -#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__use_sudo -msgid "Will use sudo based on server settings.If no sudo is configured will run without sudo" -msgstr "Utilizzerà sudo in base alle impostazioni del server. Se non è configurato sudo verrà eseguito senza sudo" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__p -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__use_sudo__p -msgid "With password" -msgstr "Con password" - -#. module: cetmix_tower_server -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_command_log__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template__use_sudo__n -#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_server_template_create_wizard__use_sudo__n -msgid "Without password" -msgstr "Senza password" - -#. module: cetmix_tower_server -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard_variable_value__wizard_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard_variable_value__wizard_id -#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_template_create_wizard_line__wizard_id -msgid "Wizard" -msgstr "Procedura guidata" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "Wrong action" -msgstr "Azione errata" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "You are not allowed to execute commands in wizard" -msgstr "Non si è autorizzati ad eseguire comandi nella procedura guidata" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_server_log.py:0 -#, python-format -msgid "You are not allowed to modify the server log output." -msgstr "Non è consentito modificare l'output del registro del server." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "You cannot execute an empty command" -msgstr "Non si può eseguire un comando vuoto" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0 -#, python-format -msgid "You cannot run custom code on multiple servers at once." -msgstr "Non è possibile eseguire codice personalizzato su server multipli contemporaneamente." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "" -"You need 'Manager' access to the server to override the default configuration values.\n" -" Without this access, the server's configured values will be used." -msgstr "" -"Per sovrascrivere i valori di configurazione predefiniti, è necessario l'accesso \"Manager\" al server.\n" -" Senza questo accesso, verranno utilizzati i valori configurati del server." - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/ir_actions_server.py:0 -#, python-format -msgid "You need to have 'write' access to all servers you want to run this action on." -msgstr "È necessario disporre dell'accesso in scrittura a tutti i server su cui si desidera eseguire questa azione." - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -msgid "e.g. /home/user This field does NOT support variables" -msgstr "es. /home/user Questo file NON supporta variabili" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_line_view_form -msgid "e.g. /such/much/{{ path }}, overrides command path" -msgstr "es. /such/much/{{ path }}, forza il percorso comando" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "general python functions (eg. lower(), replace(), etc.)" -msgstr "funzioni python generali (ad esempio lower(), replace(), ecc.)" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/tests/common.py:0 -#, python-format -msgid "groups_ref must be string or list of strings!" -msgstr "groups_ref deve essere stringa o lista di stringhe!" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "managers who can modify this record" -msgstr "gestori che possono modificare questo record" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "managers who can modify this server" -msgstr "gestori che possono modificare questo record" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "managers who can modify this template" -msgstr "gestori che possono modificare questo record" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "new server name" -msgstr "nome server nuovo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -msgid "optional, eg /home/{{ tower.server.username }}" -msgstr "opzionale, es. /home/{{ tower.server.username }}" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "re: regex operations (eg. re.sub, re.match, etc.)" -msgstr "re: operazioni regex (ad esempio re.sub, re.match, ecc.)" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "result: final result that will be used as a variable value" -msgstr "result: risultato finale che verrà utilizzato come valore variabile" - -#. module: cetmix_tower_server -#. odoo-python -#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0 -#, python-format -msgid "then" -msgstr "quindi" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_create_wizard_view_form -msgid "this can be changed later" -msgstr "questo può essere modificato dopo" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "undefined" -msgstr "non definito" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form -msgid "users who can access this server" -msgstr "utenti che possono accedere a questo server" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_template_view_form -msgid "users who can access this template" -msgstr "utenti che possono accedere a questo modello" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_file_template_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_key_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form -msgid "users who can view this record" -msgstr "utenti che possono vedere questo record" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_variable_view_form -msgid "value: variable value" -msgstr "valore: valore variabile" - -#. module: cetmix_tower_server -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form -#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_run_wizard_view_form -msgid "with tags" -msgstr "con etichette" - -#~ msgid "OSs" -#~ msgstr "SO" - -#~ msgid "" -#~ "\n" -#~ "

Help with Python expressions

\n" -#~ "
\n" -#~ "

\n" -#~ " Each Python code command returns the result value " -#~ "which is a dictionary.\n" -#~ "
There are two keys in the dictionary:\n" -#~ "

    \n" -#~ "
  • exit_code: Integer. Exit code of the command. \"0\" " -#~ "means success, any other value means failure. Default value is \"0\".\n" -#~ "
  • message: String. Message to be logged. Default value " -#~ "is \"None\".
  • \n" -#~ "
\n" -#~ "Here is an example of a python code command:\n" -#~ "\n" -#~ " server_name = server.name\n" -#~ " result = {\"exit_code\": 0, \"message\": \"Server name is \" + " -#~ "server_name}\n" -#~ "\n" -#~ "

\n" -#~ "
\n" -#~ "Please refer to the official documentation for more information and examples.\n" -#~ "
\n" -#~ "Various fields may use Python code or Python expressions. The\n" -#~ " following variables can be used:

\n" -#~ "
    \n" -#~ "
  • user: Current Odoo user
  • \n" -#~ "
  • env: Odoo Environment on which the action is\n" -#~ " triggered
  • \n" -#~ "
  • server: Server on which the command is run
  • \n" -#~ "
  • tower: 'cetmix.tower' helper class shortcut
  • \n" -#~ "
  • time, datetime, dateutil\n" -#~ " , timezone: useful Python libraries
  • \n" -#~ "
  • requests: Python 'requests' library. Available methods: " -#~ "'post', 'get', 'delete', 'request'
  • \n" -#~ "
  • json: Python 'json' library. Available methods: 'dumps'\n" -#~ "
  • hashlib: Python 'hashlib' library.\n" -#~ " Available methods: 'sha1', 'sha224', 'sha256', 'sha384', " -#~ "'sha512', 'sha3_224', 'sha3_256',\n" -#~ " 'sha3_384', 'sha3_512', 'shake_128', 'shake_256', 'blake2b', " -#~ "'blake2s', 'md5', 'new'
  • \n" -#~ "
  • hmac: Python 'hmac' library. Use 'new' to create HMAC " -#~ "objects.\n" -#~ " Available methods on the HMAC *object*: 'update', 'copy', " -#~ "'digest', 'hexdigest'.\n" -#~ " Module-level function: 'compare_digest'.
  • \n" -#~ "
  • UserError: Warning Exception to use with \n" -#~ " raise
  • \n" -#~ "
\n" -#~ msgstr "" -#~ "\n" -#~ "

Aiuto con le espressioni Python

\n" -#~ "
\n" -#~ "Diversi campi possono usare il codice Python o espressioni Python. " -#~ "Possono\n" -#~ " essere usate le seguenti variabili:

\n" -#~ "
    \n" -#~ "
  • user: utente Odoo attuale
  • \n" -#~ "
  • env: ambiente Oddo nel quale l'azione è\n" -#~ " attivata
  • \n" -#~ "
  • server: server in cui è eseguito il comando
  • \n" -#~ "
  • tower: scorciatoia classe aiuto 'cetmix.tower'
  • \n" -#~ "
  • time, datetime, dateutil\n" -#~ " , timezone: librerie Python utili
  • \n" -#~ "
  • requests: libreria Python 'requests'. Metodi disponibili: " -#~ "'post', 'get', 'delete', 'request'
  • \n" -#~ "
  • json: libreria Python 'json'. Metodi disponibili: " -#~ "'dumps'
  • \n" -#~ "
  • hashlib: libreria Python 'hashlib'.\n" -#~ " Metodi disponibili: 'sha1', 'sha224', 'sha256', 'sha384', " -#~ "'sha512', 'sha3_224', 'sha3_256',\n" -#~ " 'sha3_384', 'sha3_512', 'shake_128', 'shake_256', 'blake2b', " -#~ "'blake2s', 'md5', 'new'
  • \n" -#~ "
  • hmac: libreria Python 'hmac'. Utilizzare 'new' per creare " -#~ "oggetti HMAC.\n" -#~ " Metodi disponibili sull'oggetto HMAC : 'update', 'copy', " -#~ "'digest', 'hexdigest'.\n" -#~ " Funzioni a livello modulo: 'compare_digest'.
  • \n" -#~ "
  • UserError: allerta eccezione da utilizzare con \n" -#~ " raise
  • \n" -#~ "
\n" - -#~ msgid "" -#~ "# Please refer to the 'Help' tab and documentation for more information.\n" -#~ "#\n" -#~ "# You can return command result in the 'result' variable which is a " -#~ "dictionary:\n" -#~ "# result = {\"exit_code\": 0, \"message\": \"Some message\"}\n" -#~ "# default value is {\"exit_code\": 0, \"message\": None}\n" -#~ "#\n" -#~ "# Available variables:\n" -#~ "# - user: Current Odoo User\n" -#~ "# - env: Odoo Environment on which the action is triggered\n" -#~ "# - server: server on which the command is run\n" -#~ "# - tower: 'cetmix.tower' helper class\n" -#~ "# - time, datetime, dateutil, timezone: useful Python libraries\n" -#~ "# - requests: Python 'requests' library. Available methods: 'post', " -#~ "'get', 'delete', 'request'\n" -#~ "# - json: Python 'json' library. Available methods: 'dumps'\n" -#~ "# - hashlib: Python 'hashlib' library. Available methods: 'sha1', " -#~ "'sha224', 'sha256',\n" -#~ "# 'sha384', 'sha512', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', " -#~ "'shake_128',\n" -#~ "# 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'\n" -#~ "# - hmac: Python 'hmac' library. Use 'new' to create HMAC objects.\n" -#~ "# Available methods on the HMAC *object*: 'update', 'copy', 'digest', " -#~ "'hexdigest'.\n" -#~ "# Module-level function: 'compare_digest'.\n" -#~ "# - float_compare: Odoo function to compare floats based on specific " -#~ "precisions\n" -#~ "# - UserError: Warning Exception to use with raise\n" -#~ msgstr "" -#~ "# Per ulteriori informazioni, consultare la scheda \"Aiuto\" e la " -#~ "documentazione.\n" -#~ "#\n" -#~ "# È possibile restituire il risultato del comando nella variabile \"result" -#~ "\", che è un dizionario:\n" -#~ "# result = {\"exit_code\": 0, \"message\": \"Un messaggio\"}\n" -#~ "# il valore predefinito è {\"exit_code\": 0, \"message\": Nessuno}\n" -#~ "#\n" -#~ "# Variabili disponibili:\n" -#~ "# - user: utente Odoo corrente\n" -#~ "# - env: ambiente Odoo su cui viene attivata l'azione\n" -#~ "# - server: server su cui viene eseguito il comando\n" -#~ "# - tower: classe aiuto \"cetmix.tower\"\n" -#~ "# - time, datetime, dateutil, timezone: librerie Python utili\n" -#~ "# - requests: libreria Python \"requests\". Metodi disponibili: \"post\", " -#~ "\"get\", \"delete\", \"request\"\n" -#~ "# - json: libreria Python \"json\". Metodi disponibili: \"dumps\"\n" -#~ "# - hashlib: libreria Python \"hashlib\". Metodi disponibili: 'sha1', " -#~ "'sha224', 'sha256',\n" -#~ "# 'sha384', 'sha512', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', " -#~ "'shake_128',\n" -#~ "# 'shake_256', 'blake2b', 'blake2s', 'md5', 'new'\n" -#~ "# - hmac: libreria Python 'hmac'. Usa 'new' per creare oggetti HMAC.\n" -#~ "# Metodi disponibili sull'oggetto HMAC: 'update', 'copy', 'digest', " -#~ "'hexdigest'.\n" -#~ "# Funzione a livello di modulo: 'compare_digest'.\n" -#~ "# - float_compare: funzione Odoo per confrontare i float in base a " -#~ "precisioni specifiche\n" -#~ "# - UserError: avviso eccezione da utilizzare con raise\n" - -#~ msgid "" -#~ "# Run any SSH command on the target system\n" -#~ "# Examples: ls, cd, pwd, mkdir, rm\n" -#~ "# Adapt commands to your specific OS.\n" -#~ msgstr "" -#~ "# Esegui qualsiasi comando SSH sul sistema di destinazione\n" -#~ "# Esempi: ls, cd, pwd, mkdir, rm\n" -#~ "# Adatta i comandi al tuo sistema operativo specifico.\n" - -#~ msgid "Host key" -#~ msgstr "Chiave host" - -#~ msgid "1 day" -#~ msgstr "1 giorno" - -#~ msgid "1 hour" -#~ msgstr "1 ora" - -#~ msgid "1 week" -#~ msgstr "1 settimana" - -#~ msgid "1 year" -#~ msgstr "1 anno" - -#~ msgid "10 min" -#~ msgstr "10 minuti" - -#~ msgid "12 hour" -#~ msgstr "12 ore" - -#~ msgid "2 hour" -#~ msgstr "2 ore" - -#~ msgid "30 min" -#~ msgstr "30 minuti" - -#~ msgid "6 hour" -#~ msgstr "6 ore" - -#~ msgid "" -#~ "\n" -#~ " x = 2*10\n" -#~ " COMMAND_RESULT = {\"exit_code\": x, " -#~ "\"message\": \"This will be\n" -#~ " logged as an error message because " -#~ "exit code !=0\"}\n" -#~ "" -#~ msgstr "" -#~ "\n" -#~ " x = 2*10\n" -#~ " COMMAND_RESULT = {\"exit_code\": x, " -#~ "\"message\": \"Questo verrà\n" -#~ " registrato come un messaggio di " -#~ "errore perché il codice di uscita è diverso da 0\"}\n" -#~ "" - -#~ msgid "" -#~ "UserError: Warning Exception to use with \n" -#~ " raise" -#~ msgstr "" -#~ "UserError: Avviso eccezione da utilizzare con \n" -#~ " raise" - -#~ msgid "" -#~ "env: Odoo Environment on which the action is\n" -#~ " triggered" -#~ msgstr "" -#~ "env: ambiente Odoo in cui è attivata\n" -#~ " l'azione" - -#~ msgid "server: Server on which the command is run" -#~ msgstr "server: Server in cui è eseguito il comando" - -#~ msgid "" -#~ "time, datetime, dateutil\n" -#~ " , timezone: useful " -#~ "Python libraries" -#~ msgstr "" -#~ "time, datetime, dateutil\n" -#~ " , timezone: librarie " -#~ "Python utili" - -#~ msgid "tower: 'cetmix.tower' helper class shortcut" -#~ msgstr "tower: scorciatoia classe aiuto 'cetmix.tower'" - -#~ msgid "show shared" -#~ msgstr "visualizza condivisi" - -#~ msgid "Any Server" -#~ msgstr "Qualsiasi server" - -#~ msgid "Command Preview" -#~ msgstr "Anteprima comando" - -#~ msgid "" -#~ "Each python code command returns the COMMAND_RESULT value\n" -#~ " which is a dictionary." -#~ msgstr "" -#~ "Ogni comando in codice Python restituisce il valore COMMAND_RESULT \n" -#~ " che è un dizionario." - -#~ msgid "" -#~ "Error loading a private key. Unsupported key format or incorrect key." -#~ msgstr "" -#~ "Errore durante il caricamento di una chiave privata. Formato chiave non " -#~ "supportato o chiave errata." - -#~ msgid "Execute Command" -#~ msgstr "Eseguire comando" - -#~ msgid "Execute Result" -#~ msgstr "Esegui risultato" - -#~ msgid "File name" -#~ msgstr "Nome file" - -#~ msgid "Followers (Channels)" -#~ msgstr "Chi segue (canali)" - -#~ msgid "Help with Python expressions" -#~ msgstr "Aiuto con le espressioni Python" - -#~ msgid "" -#~ "If enabled command can be run on the same server while the same command " -#~ "is still running.\n" -#~ "Returns ANOTHER_COMMAND_RUNNING if execution is blocked" -#~ msgstr "" -#~ "Se abilitata, il comando può essere eseguito sullo stesso server mentre " -#~ "lo stesso comando è ancora in esecuzione. \n" -#~ "Restituisce ANOTHER_COMMAND_RUNNING se l'esecuzione è bloccata" - -#~ msgid "" -#~ "If enabled flightplan can be run on the same server while the same " -#~ "flightplan is still running.\n" -#~ "Returns -5 status is execution is blocked" -#~ msgstr "" -#~ "Se abilitata, il piano di volo può essere eseguito sullo stesso server " -#~ "mentre lo stesso piano di volo è ancora in esecuzione.\n" -#~ "Restituisce -5 stato se l'esecuzione è bloccata" - -#~ msgid "Leave blank to use for any partner" -#~ msgstr "Lasciare vuoto per usare per tutti i partner" - -#~ msgid "Leave blank to use with any partner" -#~ msgstr "Lasciare vuoto per usare con ogni partner" - -#~ msgid "Leave blank to use with any server" -#~ msgstr "Lasciare vuoto per usare con ogni server" - -#~ msgid "Mixin for managing secrets" -#~ msgstr "Mixin per la gestione dei segreti" - -#~ msgid "No sudo" -#~ msgstr "Nessun sudo" - -#~ msgid "Number of unread messages" -#~ msgstr "Numero di messaggi non letti" - -#~ msgid "Reference must be unique for the combination of partner and server" -#~ msgstr "" -#~ "Il riferimento deve essere univoco per la combinazione partner e server" - -#~ msgid "Select tags to filter Commands" -#~ msgstr "Selezionare etichette per filtrare i comandi" - -#~ msgid "Sudo with password" -#~ msgstr "Sudo con password" - -#~ msgid "Sudo without password" -#~ msgstr "Sudo senza password" - -#~ msgid "There are two default keys in the dictionary, e.g.:" -#~ msgstr "Ci sono due chiavi predefinite nel dizionario, es.:" - -#~ msgid "Tower automation helper model" -#~ msgstr "Modello aiuto automazione Tower" - -#~ msgid "Unread Messages Counter" -#~ msgstr "Contatore messaggi non letti" - -#~ msgid "Used for selected server only. Leave blank to use globally" -#~ msgstr "" -#~ "Utilizza solo per i server selezionati. Lasciare vuoto per usare " -#~ "globalmente" - -#~ msgid "" -#~ "Various fields may use Python code or Python expressions. The\n" -#~ " following variables can be used:" -#~ msgstr "" -#~ "Vari campi possono utilizzare codice Python o espressioni Python. \n" -#~ " Possono essere utilizzate le seguenti " -#~ "variabili:" - -#~ msgid "_execute() function takes single server record only!" -#~ msgstr "La funzione _execute() utilizza solo record server singolo!" - -#~ msgid "sudo password was not provided!" -#~ msgstr "la password sudo non è stata fornita!" - -#~ msgid "SSH execute command error" -#~ msgstr "Errore comando esecuzione SSH" - -#~ msgid "" -#~ "\n" -#~ " Server monitoring and basic actions.\n" -#~ " " -#~ msgstr "" -#~ "\n" -#~ " Monitoraggio server e azioni di base.\n" -#~ " " - -#~ msgid "Cetmix Tower Execute Flight Plan" -#~ msgstr "Esegui piano di volo Cetmix Tower" - -#~ msgid "Execute Flight Plan" -#~ msgstr "Eseguire piano di volo" - -#~ msgid "Execute New Command" -#~ msgstr "Esegui nuovo comando" - -#~ msgid "Execute Plan" -#~ msgstr "Esegui piano" - -#~ msgid "Parent Flight Plan Log" -#~ msgstr "Registro piano di volo padre" - -#~ msgid "Rendered code is shown as for the first selected server!" -#~ msgstr "" -#~ "Il codice realizzato viene mostrato come per il primo server selezionato!" diff --git a/addons/cetmix_tower_server/migrations/16.0.1.0.6/post-migration.py b/addons/cetmix_tower_server/migrations/16.0.1.0.6/post-migration.py deleted file mode 100644 index f8a6336..0000000 --- a/addons/cetmix_tower_server/migrations/16.0.1.0.6/post-migration.py +++ /dev/null @@ -1,21 +0,0 @@ -import logging - -from odoo import SUPERUSER_ID, api - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - """ - Generate references for files. - """ - - _logger.info("Starting reference generation for files.") - env = api.Environment(cr, SUPERUSER_ID, {}) - model_obj = env["cx.tower.file"] - records_without_reference = model_obj.search([("reference", "=", False)]) - for record in records_without_reference: - record_reference = record._generate_or_fix_reference(record.name) - record.write({"reference": record_reference}) - _logger.info(f"Generated reference for file {record.name}: {record_reference}") - _logger.info("Reference generation for files completed.") diff --git a/addons/cetmix_tower_server/migrations/16.0.2.0.0/post-migration.py b/addons/cetmix_tower_server/migrations/16.0.2.0.0/post-migration.py deleted file mode 100644 index 3c193b6..0000000 --- a/addons/cetmix_tower_server/migrations/16.0.2.0.0/post-migration.py +++ /dev/null @@ -1,119 +0,0 @@ -import logging - -from odoo import SUPERUSER_ID, api - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - """ - Move SSH credentials, host keys, SSH keys, and secret values - to the vault-backed storage. - - """ - - # 1. SSH password and host key are now stored in secrets - _logger.info("Moving SSH password and host key to vault.") - env = api.Environment(cr, SUPERUSER_ID, {}) - # Read SSH password and host key from servers using SQL query - cr.execute( - """ - SELECT id, ssh_password, host_key - FROM cx_tower_server - WHERE ssh_password IS NOT NULL OR host_key IS NOT NULL - """ - ) - server_records = cr.fetchall() - server_model = env["cx.tower.server"] - success = False - try: - for record in server_records: - _logger.info( - f"Moving SSH password and host key to vault for server {record[0]}" - ) - server_model.browse(record[0]).write( - {"ssh_password": record[1], "host_key": record[2]} - ) - _logger.info("Moving SSH password and host key to vault completed.") - success = True - # Clear SSH password and host key from servers - except Exception as e: - _logger.error(f"Error moving SSH password and host key to vault: {e}") - raise e - finally: - if success: - cr.execute( - """ - UPDATE cx_tower_server - SET ssh_password = NULL, host_key = NULL - WHERE ssh_password IS NOT NULL OR host_key IS NOT NULL - """ - ) - _logger.info("Cleared SSH password and host key from servers.") - - # 2. SSH keys are now stored in secrets - _logger.info("Moving SSH keys to vault.") - success = False - # Read SSH keys from keys using SQL query - cr.execute( - """ - SELECT id, secret_value - FROM cx_tower_key - WHERE key_type = 'k' - """ - ) - ssh_key_records = cr.fetchall() - ssh_key_model = env["cx.tower.key"] - try: - for record in ssh_key_records: - _logger.info(f"Moving SSH key to vault record {record[0]}") - ssh_key_model.browse(record[0]).write({"secret_value": record[1]}) - _logger.info("Moving SSH keys to vault completed.") - success = True - except Exception as e: - _logger.error(f"Error moving SSH keys to vault: {e}") - raise e - finally: - if success: - # Clear SSH key from keys - cr.execute( - """ - UPDATE cx_tower_key - SET secret_value = NULL - WHERE secret_value IS NOT NULL - """ - ) - _logger.info("Cleared SSH key from keys.") - - # 3. Secret values are now stored in secrets - _logger.info("Moving secret values to vault.") - success = False - # Read secret values from key values using SQL query - cr.execute( - """ - SELECT id, secret_value - FROM cx_tower_key_value - """ - ) - secret_value_records = cr.fetchall() - secret_value_model = env["cx.tower.key.value"] - try: - for record in secret_value_records: - _logger.info(f"Moving secret value to vault record {record[0]}") - secret_value_model.browse(record[0]).write({"secret_value": record[1]}) - _logger.info("Moving secret values to vault completed.") - success = True - except Exception as e: - _logger.error(f"Error moving secret values to vault: {e}") - raise e - finally: - if success: - # Clear secret value from key values - cr.execute( - """ - UPDATE cx_tower_key_value - SET secret_value = NULL - WHERE secret_value IS NOT NULL - """ - ) - _logger.info("Cleared secret value from key values.") diff --git a/addons/cetmix_tower_server/models/__init__.py b/addons/cetmix_tower_server/models/__init__.py deleted file mode 100644 index 88aab1c..0000000 --- a/addons/cetmix_tower_server/models/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - -from . import cx_tower_variable_mixin -from . import cx_tower_template_mixin -from . import cx_tower_access_mixin -from . import cx_tower_access_role_mixin -from . import cx_tower_reference_mixin -from . import cx_tower_tag_mixin -from . import cx_tower_key_mixin -from . import cx_tower_vault_mixin -from . import cx_tower_vault -from . import cx_tower_variable -from . import cx_tower_variable_value -from . import cx_tower_file -from . import cx_tower_file_template -from . import cx_tower_server -from . import cx_tower_os -from . import cx_tower_tag -from . import cx_tower_command -from . import cx_tower_custom_variable_value_mixin -from . import cx_tower_key -from . import cx_tower_key_value -from . import cx_tower_command_log -from . import cx_tower_plan -from . import cx_tower_plan_line -from . import cx_tower_plan_line_action -from . import cx_tower_plan_log -from . import cx_tower_server_log -from . import cx_tower_server_template -from . import cx_tower_shortcut -from . import cx_tower_scheduled_task -from . import cx_tower_scheduled_task_cv -from . import cetmix_tower -from . import cx_tower_variable_option -from . import ir_actions_server -from . import res_config_settings -from . import res_partner diff --git a/addons/cetmix_tower_server/models/cetmix_tower.py b/addons/cetmix_tower_server/models/cetmix_tower.py deleted file mode 100644 index 3a2d224..0000000 --- a/addons/cetmix_tower_server/models/cetmix_tower.py +++ /dev/null @@ -1,300 +0,0 @@ -# Copyright (C) 2024 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging -import time - -from odoo import _, api, models -from odoo.exceptions import ValidationError - -from .constants import NOT_FOUND, SSH_CONNECTION_ERROR - -_logger = logging.getLogger(__name__) - - -class CetmixTower(models.AbstractModel): - """Generic model used to simplify Odoo automation. - - Used to keep main integration function in a single place. - - For example when writing automated actions one can use - `env["cetmix.tower"].create_server_from_template(..)` - instead of - `env["cx.tower.server.template"].create_server_from_template(..) - """ - - _name = "cetmix.tower" - _description = "Cetmix Tower Odoo Automation" - - @api.model - def server_create_from_template(self, template_reference, server_name, **kwargs): - """Shortcut for the same method of the 'cx.tower.server.template' model. - - Important! Add dedicated tests for this function if modified later. - """ - return self.env["cx.tower.server.template"].create_server_from_template( - template_reference=template_reference, server_name=server_name, **kwargs - ) - - @api.model - def server_run_command( - self, server_reference, command_reference, get_result=True, **variable_values - ): - """Run command on selected server. - - Args: - server_reference (Char): Server reference - command_reference (Char): Command reference - get_result (bool, optional): Get the result of the command. - If False, the result will be saved to the log. - Defaults to True. - - **variable_values: - Dict: with variable values. - The keys are the variable references and the values are the variable values. - eg `{'odoo_version': '16.0'}` - - Returns: - Dict: with two keys if `get_result` is True: - - exit_code (Int): Exit code of the command - - message (Char): Message of the command - """ - - server = self.env["cx.tower.server"].get_by_reference(server_reference) - if not server: - return {"exit_code": NOT_FOUND, "message": _("Server not found")} - command = self.env["cx.tower.command"].get_by_reference(command_reference) - if not command: - return {"exit_code": NOT_FOUND, "message": _("Command not found")} - - # Will return command result if get_result is True - # Otherwise will save to log and return None - command_result = server.with_context(no_command_log=get_result).run_command( - command, **{"variable_values": variable_values} if variable_values else {} - ) - - # Return command result if get_result is True - if command_result: - status = command_result.get("status") - response = command_result.get("response", "") - error = command_result.get("error", "") - return { - "exit_code": status, - "message": response or error, - } - - def server_run_flight_plan( - self, server_reference, flight_plan_reference, **variable_values - ): - """Run flight plan on selected server. - - Args: - server_reference (Char): Server reference - flight_plan_reference (Char): Flight plan reference - - **variable_values: - Dict: with variable values. - The keys are the variable references and the values are the variable values. - eg `{'odoo_version': '16.0'}` - - Returns: - cx.tower.plan.log(): flight plan log record or False if error - """ - server = self.env["cx.tower.server"].get_by_reference(server_reference) - if not server: - # This is not the best way to handle this, but it's the only way to - # avoid complex response handling - return False - flight_plan = self.env["cx.tower.plan"].get_by_reference(flight_plan_reference) - if not flight_plan: - # This is not the best way to handle this, but it's the only way to - # avoid complex response handling - return False - return server.run_flight_plan( - flight_plan, - **{"variable_values": variable_values} if variable_values else {}, - ) - - @api.model - def server_set_variable_value(self, server_reference, variable_reference, value): - """Set variable value for selected server. - Modifies existing variable value or creates a new one. - - Args: - server_reference (Char): Server reference - variable_reference (Char): Variable reference - value (Char): Variable value - - Returns: - Dict: with who keys: - - exit_code (Char) - - message (Char) - """ - - server = self.env["cx.tower.server"].get_by_reference(server_reference) - if not server: - return {"exit_code": NOT_FOUND, "message": _("Server not found")} - variable = self.env["cx.tower.variable"].get_by_reference(variable_reference) - if not variable: - return {"exit_code": NOT_FOUND, "message": _("Variable not found")} - - # Check if variable is already defined for the server - variable_value_record = variable.value_ids.filtered( - lambda v: v.server_id == server - ) - if variable_value_record: - variable_value_record.value_char = value - result = {"exit_code": 0, "message": _("Variable value updated")} - - else: - self.env["cx.tower.variable.value"].create( - { - "variable_id": variable.id, - "server_id": server.id, - "value_char": value, - } - ) - result = {"exit_code": 0, "message": _("Variable value created")} - return result - - @api.model - def server_get_variable_value( - self, server_reference, variable_reference, check_global=True - ): - """Get variable value for selected server. - - Args: - server_reference (Char): Server reference - variable_reference (Char): Variable reference - check_global (bool, optional): Check for global value if variable - is not defined for selected server. Defaults to True. - Returns: - Char: variable value or None - """ - - # Get server by reference - server = self.env["cx.tower.server"].get_by_reference(server_reference) - if not server: - return None - result = self.env["cx.tower.variable.value"].get_by_variable_reference( - variable_reference=variable_reference, - server_id=server.id, - check_global=check_global, - ) - - # Get server defined value first - value = result.get("server") - - # Get global value if value is not set - if not value and check_global: - value = result.get("global") - return value - - @api.model - def server_check_ssh_connection( - self, - server_reference, - attempts=5, - wait_time=10, - try_command=True, - try_file=True, - ): - """Check if SSH connection to the server is available. - This method only checks if the connection is available, - it does not execute any commands to check if they are working. - - Args: - server_reference (Char): Server reference. - attempts (int): Number of attempts to try the connection. - Default is 5. - wait_time (int): Wait time in seconds between connection attempts. - Default is 10 seconds. - try_command (bool): Try to execute a command. - Default is True. - try_file (bool): Try file operations. - Default is True. - Raises: - ValidationError: - If the provided server reference is invalid or - the server cannot be found. - Returns: - dict: { - "exit_code": int, - 0 for success, - error code for failure - "message": str # Description of the result - } - """ - server = self.env["cx.tower.server"].get_by_reference(server_reference) - if not server: - raise ValidationError(_("No server found for the provided reference.")) - - # Try connecting multiple times - for attempt in range(1, attempts + 1): - try: - _logger.info( - "Attempt %s of %s to connect to server %s", - attempt, - attempts, - server_reference, - ) - result = server.test_ssh_connection( - raise_on_error=True, - return_notification=False, - try_command=try_command, - try_file=try_file, - ) - if result.get("status") == 0: - return { - "exit_code": 0, - "message": _("Connection successful."), - } - if attempt == attempts: - return { - "exit_code": SSH_CONNECTION_ERROR, - "message": _( - "Failed to connect after %(attempts)s attempts. " - "Error: %(err)s", - attempts=attempts, - err=result.get("error", ""), - ), - } - except Exception as e: # pylint: disable=broad-except - if attempt == attempts: - return { - "exit_code": SSH_CONNECTION_ERROR, - "message": _("Failed to connect. Error: %(err)s", err=e), - } - time.sleep(wait_time) - - @api.model - def server_validate_secret( - self, secret_value, secret_reference, server_reference=None - ): - """ - Validate the provided secret value against the actual secret. - - Accepts either a full inline reference (e.g. #!cxtower.secret.!#) - or just a . - - Args: - secret_value (Char): Value to validate - secret_reference (Char): Reference code or inline reference - server_reference (Char, optional): Reference code of the server - Returns: - Bool: True if the value matches the secret, False otherwise - """ - server = self.env["cx.tower.server"] - if server_reference: - server = server.get_by_reference(server_reference) - - # Try to extract reference from inline format using _extract_key_parts - key_parts = self.env["cx.tower.key"]._extract_key_parts(secret_reference) - if key_parts: - # _extract_key_parts returns a tuple: (key_type, reference). - # We only need the reference part here. - secret_reference = key_parts[1] - - value = self.env["cx.tower.key"]._resolve_key_type_secret( - secret_reference, server_id=server.id - ) - return value == secret_value diff --git a/addons/cetmix_tower_server/models/constants.py b/addons/cetmix_tower_server/models/constants.py deleted file mode 100644 index a0f121e..0000000 --- a/addons/cetmix_tower_server/models/constants.py +++ /dev/null @@ -1,125 +0,0 @@ -from odoo import _ - -# *** -# This file is used to define commonly used constants -# *** - -# Returned when a general error occurs -GENERAL_ERROR = -100 - -# Returned when a resource is not found -NOT_FOUND = -101 - -# -- SSH - -# Returned when an SSH connection error occurs -SSH_CONNECTION_ERROR = 503 - -# -- Command: -200 > -299 - -# Returned when trying to execute another instance of a command on the same server -# and this command doesn't allow parallel run -ANOTHER_COMMAND_RUNNING = -201 - -# Returned when no runner is found for command action -NO_COMMAND_RUNNER_FOUND = -202 - -# Returned when the command failed to execute due to a python code execution error -PYTHON_COMMAND_ERROR = -203 - -# Returned when the command failed to execute because the condition was not met -PLAN_LINE_CONDITION_CHECK_FAILED = -205 - -# Returned when the command timed out -COMMAND_TIMED_OUT = -206 -COMMAND_TIMED_OUT_MESSAGE = _("Command timed out and was terminated") - -# Returned when the command is not compatible with the server -COMMAND_NOT_COMPATIBLE_WITH_SERVER = -207 - -# Returned when the command was stopped by user -COMMAND_STOPPED = -208 - -# -- Plan: -300 > -399 - -# Returned when trying to execute another instance of a flightplan on the same server -# and this flightplan doesn't allow parallel run -ANOTHER_PLAN_RUNNING = -301 - -# Returned when trying to start plan without lines -PLAN_IS_EMPTY = -302 - -# Returned when a plan tries to parse a command log record which doesn't have -# a valid plan reference in it -PLAN_NOT_ASSIGNED = -303 - -# Returned when a plan tries to parse a command log record which doesn't have -# a valid plan line reference in it -PLAN_LINE_NOT_ASSIGNED = -304 - -# Returned when any of the commands in the plan is not compatible with the server -PLAN_NOT_COMPATIBLE_WITH_SERVER = -306 - -# Returned when the flight plan was stopped by user -PLAN_STOPPED = -308 - -# -- File: -400 > -499 - -# Returned when the file could not be created on the server -FILE_CREATION_FAILED = -400 - -# Returned when the file could not be uploaded to the server -FILE_UPLOAD_FAILED = -401 - -# Returned when the file could not be downloaded from the server -FILE_DOWNLOAD_FAILED = -402 - -# -- Default values - -# Default Python code used in Python code command -DEFAULT_PYTHON_CODE = _( - """# Please refer to the 'Help' tab and documentation for more information. -# -# You can return command result in the 'result' variable which is a dictionary: -# result = {"exit_code": 0, "message": "Some message"} -# default value is {"exit_code": 0, "message": None} -""" # noqa: E501 -) - - -# Default Python code help displayed in the "Help" tab -DEFAULT_PYTHON_CODE_HELP = _( - """ -

Help with Python expressions

-
-

- Each Python code command returns the result value which is a dictionary. -
There are two keys in the dictionary: -

    -
  • exit_code: Integer. Exit code of the command. "0" means success, any other value means failure. Default value is "0".
  • -
  • message: String. Message to be logged. Default value is "None".
  • -
-You can also access the custom_values dictionary that contains custom values provided to the command or flight plan. -Custom values can be modified, thus can be used to pass data between commands in a flight plan. -Please keep in mind that custom values are persistent only between commands in a flight plan and are not saved to the database. -
-Here is an example of a python code command: - - - server_name = server.name - build_name = custom_values.get("build_name") - if build_name: - result = {"exit_code": 0, "message": "Build name for " + server_name + " is " + build_name} - else: - result = {"exit_code": 0, "message": "No build name provided for " + server_name} - custom_values["build_name"] = "New build name" - -

-
-Please refer to the official documentation for more information and examples. -
-

Various fields may use Python code or Python expressions. The - following variables can be used:

-""" # noqa: E501 -) diff --git a/addons/cetmix_tower_server/models/cx_tower_access_mixin.py b/addons/cetmix_tower_server/models/cx_tower_access_mixin.py deleted file mode 100644 index 7ad844e..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_access_mixin.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class CxTowerAccessMixin(models.AbstractModel): - """Used to implement template access levels in models.""" - - _name = "cx.tower.access.mixin" - _description = "Cetmix Tower access mixin" - - access_level = fields.Selection( - lambda self: self._selection_access_level(), - default=lambda self: self._default_access_level(), - required=True, - index=True, - ) - - def _selection_access_level(self): - """Available access levels - - Returns: - List of tuples: available options. - """ - return [ - ("1", "User"), - ("2", "Manager"), - ("3", "Root"), - ] - - def _default_access_level(self): - """Default access level - - Returns: - Char: `access_level` field selection value - """ - return "2" diff --git a/addons/cetmix_tower_server/models/cx_tower_access_role_mixin.py b/addons/cetmix_tower_server/models/cx_tower_access_role_mixin.py deleted file mode 100644 index 37c3b9e..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_access_role_mixin.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (C) 2025 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models - - -class CxTowerAccessRoleMixin(models.AbstractModel): - """Used to implement access roles in models.""" - - _name = "cx.tower.access.role.mixin" - _description = "Cetmix Tower access role mixin" - - # IMPORTANT: inherit these fields in your model - # add 'relation' key explicitly to the field. - # Use 'cx.tower.server' as model as a reference. - user_ids = fields.Many2many( - comodel_name="res.users", - column1="record_id", - column2="user_id", - string="Users", - domain=lambda self: [ - ("groups_id", "in", [self.env.ref("cetmix_tower_server.group_user").id]) - ], - default=lambda self: self._default_user_ids(), - help="Users who can view this record", - copy=False, - ) - - manager_ids = fields.Many2many( - comodel_name="res.users", - column1="record_id", - column2="manager_id", - string="Managers", - groups="cetmix_tower_server.group_manager", - domain=lambda self: [ - ("groups_id", "in", [self.env.ref("cetmix_tower_server.group_manager").id]) - ], - default=lambda self: self._default_manager_ids(), - help="Managers who can modify this record", - copy=False, - ) - - def _default_user_ids(self): - """ - Default Users for new Records. - """ - # If user is in group_user, add them to the list - if self.env.user.has_group("cetmix_tower_server.group_user"): - return [self.env.user.id] - # Otherwise, return an empty list. Eg if created using sudo() - return [] - - def _default_manager_ids(self): - """ - Default Managers for new Records. - """ - # If user is manager, add them to the list - if self.env.user.has_group("cetmix_tower_server.group_manager"): - return [self.env.user.id] - # Otherwise, return an empty list. Eg if created using sudo() - return [] - - @api.model_create_multi - def create(self, vals_list): - """ - Create records with post-create fields. - """ - post_create_fields = self._get_post_create_fields() - post_create_vals_list = [] - for vals in vals_list: - post_create_vals = {} - for key in post_create_fields: - if key in vals: - post_create_vals[key] = vals.pop(key) - post_create_vals_list.append(post_create_vals) - - # Create records without post-create fields - res = super().create(vals_list) - if post_create_vals_list: - # Create related records with post-create field - for post_create_vals, record in zip(post_create_vals_list, res): # noqa: B905 we need to run on Python 3.10 - if post_create_vals: - record.write(post_create_vals) - - return res - - def _get_post_create_fields(self): - """ - Get post-create fields. - - Some records may create related records which use rules - that depend on `user_ids` and `manager_ids` fields. - However at the moment of record creation, these fields are not yet set. - So first we create the record without these fields, then we create - the related records to avoid access violations. - - Returns: - list: List of fields to be set after record creation. - """ - return [] diff --git a/addons/cetmix_tower_server/models/cx_tower_command.py b/addons/cetmix_tower_server/models/cx_tower_command.py deleted file mode 100644 index fa72ac1..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_command.py +++ /dev/null @@ -1,550 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from types import SimpleNamespace - -from dns import exception, resolver, reversename -from pytz import timezone - -from odoo import _, api, fields, models, tools -from odoo.exceptions import UserError -from odoo.tools import ormcache -from odoo.tools.float_utils import float_compare -from odoo.tools.safe_eval import wrap_module - -from .constants import DEFAULT_PYTHON_CODE, DEFAULT_PYTHON_CODE_HELP - -requests = wrap_module(__import__("requests"), ["post", "get", "delete", "request"]) -json = wrap_module(__import__("json"), ["dumps"]) -hashlib = wrap_module( - __import__("hashlib"), - [ - "sha1", - "sha224", - "sha256", - "sha384", - "sha512", - "sha3_224", - "sha3_256", - "sha3_384", - "sha3_512", - "shake_128", - "shake_256", - "blake2b", - "blake2s", - "md5", - "new", - ], -) -hmac = wrap_module( - __import__("hmac"), - ["new", "compare_digest"], -) -tldextract = wrap_module(__import__("tldextract"), ["extract"]) -dns_resolver = wrap_module(resolver, ["resolve", "query"]) -dns_reversename = wrap_module(reversename, ["from_address", "to_address"]) -dns_exception = wrap_module(exception, ["DNSException"]) - - -dns = SimpleNamespace( - resolver=dns_resolver, - reversename=dns_reversename, - exception=dns_exception, -) - - -class CxTowerCommand(models.Model): - """Command to run on a server""" - - _name = "cx.tower.command" - _inherit = [ - "cx.tower.template.mixin", - "cx.tower.reference.mixin", - "cx.tower.access.mixin", - "cx.tower.access.role.mixin", - "cx.tower.key.mixin", - "cx.tower.tag.mixin", - ] - _description = "Cetmix Tower Command" - _order = "name" - - active = fields.Boolean(default=True) - allow_parallel_run = fields.Boolean( - help="If enabled, multiple instances of the same command " - "can be run on the same server at the same time.\n" - "Otherwise, ANOTHER_COMMAND_RUNNING status will be returned if another" - " instance of the same command is already running" - ) - server_ids = fields.Many2many( - comodel_name="cx.tower.server", - relation="cx_tower_server_command_rel", - column1="command_id", - column2="server_id", - string="Servers", - help="Servers on which the command will be run.\n" - "If empty, command can be run on all servers", - ) - tag_ids = fields.Many2many( - relation="cx_tower_command_tag_rel", - column1="command_id", - column2="tag_id", - ) - os_ids = fields.Many2many( - comodel_name="cx.tower.os", - relation="cx_tower_os_command_rel", - column1="command_id", - column2="os_id", - string="OSes", - ) - note = fields.Text() - - action = fields.Selection( - selection=lambda self: self._selection_action(), - required=True, - default=lambda self: self._selection_action()[0][0], - ) - path = fields.Char( - string="Default Path", - help="Location where command will be run. " - "You can use {{ variables }} in path", - ) - file_template_id = fields.Many2one( - comodel_name="cx.tower.file.template", - help="This template will be used to create or update the pushed file", - ) - template_code = fields.Text( - string="Template Code", - related="file_template_id.code", - readonly=True, - help="Code of the associated file template", - ) - flight_plan_line_ids = fields.One2many( - comodel_name="cx.tower.plan.line", - related="flight_plan_id.line_ids", - readonly=True, - help="Lines of the associated flight plan", - ) - code = fields.Text( - compute="_compute_code", - store=True, - readonly=False, - ) - command_help = fields.Html( - compute="_compute_command_help", - compute_sudo=True, - ) - flight_plan_id = fields.Many2one( - comodel_name="cx.tower.plan", - help="Flight plan run by the command", - ) - flight_plan_used_ids = fields.Many2many( - comodel_name="cx.tower.plan", - help="Flight plan this command is used in", - relation="cx_tower_command_flight_plan_used_id_rel", - column1="command_id", - column2="plan_id", - store=True, - copy=False, - ) - flight_plan_used_ids_count = fields.Integer( - compute="_compute_flight_plan_used_ids_count", - help="Flight plan this command is used in", - ) - server_status = fields.Selection( - selection=lambda self: self.env["cx.tower.server"]._selection_status(), - help="Set the following status if command finishes with success. " - "Leave 'Undefined' if you don't need to update the status", - ) - no_split_for_sudo = fields.Boolean( - string="No Split for sudo", - help="If enabled, do not split command on '&&' when using sudo." - "Prepend sudo once to the whole command.", - ) - variable_ids = fields.Many2many( - comodel_name="cx.tower.variable", - relation="cx_tower_command_variable_rel", - column1="command_id", - column2="variable_id", - ) - - if_file_exists = fields.Selection( - selection=[ - ("skip", "Skip"), - ("overwrite", "Overwrite"), - ("raise", "Raise Error"), - ], - default="skip", - help="What to do if file already exists on the server.\n" - "- Skip: Do not create or update the file.\n" - "- Overwrite: Replace the existing file with the new one.\n" - "- Raise Error: Raise an error if the file already exists.", - ) - disconnect_file = fields.Boolean( - string="Disconnect from Template", - help=( - "If enabled, disconnects the file from its template " - "after running the command.\n" - ), - ) - - # ---- Access. Add relation for mixin fields - user_ids = fields.Many2many( - relation="cx_tower_command_user_rel", - ) - manager_ids = fields.Many2many( - relation="cx_tower_command_manager_rel", - ) - - @classmethod - def _get_depends_fields(cls): - """ - Define dependent fields for computing `variable_ids` in command-related models. - - This implementation specifies that the fields `code` and `path` - are used to determine the variables associated with a command. - - Returns: - list: A list of field names (str) representing the dependencies. - - Example: - The following fields trigger recomputation of `variable_ids`: - - `code`: The command's script or running logic. - - `path`: The default running path for the command. - """ - return ["code", "path"] - - # -- Selection - def _selection_action(self): - """Actions that can be run by a command. - - Returns: - List of tuples: available options. - """ - return [ - ("ssh_command", "SSH command"), - ("python_code", "Run Python code"), - ("file_using_template", "Create file using template"), - ("plan", "Run flight plan"), - ] - - # -- Defaults - def _get_default_python_code(self): - """ - Default python command code - """ - return DEFAULT_PYTHON_CODE - - def _get_default_python_code_help(self): - """ - Default python code help - """ - - # Available libraries are Odoo objects + Python libraries - available_libraries = self._get_python_command_odoo_objects() - available_libraries.update(self._get_python_command_libraries()) - help_text_fragments = [] - for key, value in available_libraries.items(): - help_text_fragments.append(f"
  • {key}: {value['help']}
  • ") - - help_text_fragments.append( - f"
  • custom_values: {_('Flight plan custom values')}
  • " - ) - - help_text = "
      " + "".join(help_text_fragments) + "
    " - return f"{DEFAULT_PYTHON_CODE_HELP}{help_text}" - - # -- Computes - @api.depends("action") - def _compute_code(self): - """ - Compute default code - """ - default_python_code = self._get_default_python_code() - for command in self: - if command.action == "python_code": - command.code = default_python_code - continue - command.code = False - - @api.depends("action") - def _compute_command_help(self): - """ - Compute command help - """ - default_python_code_help = self._get_default_python_code_help() - for command in self: - if command.action == "python_code": - command.command_help = default_python_code_help - else: - command.command_help = False - - @api.depends("flight_plan_used_ids") - def _compute_flight_plan_used_ids_count(self): - """ - Compute flight plan ids count - """ - for command in self: - command.flight_plan_used_ids_count = len(command.flight_plan_used_ids) - - def action_open_command_logs(self): - """ - Open current current command log records - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_command_log" - ) - action["domain"] = [("command_id", "=", self.id)] - return action - - def action_open_plans(self): - """ - Open plans this command is used in - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_plan" - ) - action["domain"] = [("id", "in", self.flight_plan_used_ids.ids)] - return action - - def _check_server_compatibility(self, server): - """Check if the command is compatible with the server - Args: - server (cx.tower.server()): Server object - - Returns: - bool: True if the command is compatible with the server, False otherwise - """ - self.ensure_one() - return not self.server_ids or server.id in self.server_ids.ids - - # -- Business logic - @ormcache() - @api.model - def _get_python_command_libraries(self): - """ - Get available python imports. Use this method to import python libraries. - Please be advised, that this method is cached. - If you need to use a non-cached import, eg for Odoo objects, - use the `_get_python_command_odoo_objects` method instead. - - - Returns: - dict: Available libraries: - {"": { - "import": , - "help": - }} - """ - python_libraries = { - "time": { - "import": tools.safe_eval.time, - "help": _("Python 'time' library"), - }, - "datetime": { - "import": tools.safe_eval.datetime, - "help": _("Python 'datetime' library"), - }, - "dateutil": { - "import": tools.safe_eval.dateutil, - "help": _("Python 'dateutil' library"), - }, - "timezone": { - "import": timezone, - "help": _("Python 'timezone' library"), - }, - "requests": { - "import": requests, - "help": _( - "Python 'requests' library. Available methods: 'post', 'get'," - " 'delete', 'request'" - ), - }, - "json": { - "import": json, - "help": _("Python 'json' library. Available methods: 'dumps'"), - }, - "float_compare": { - "import": float_compare, - "help": _("Float compare. Odoo helper function to compare floats."), - }, - "UserError": { - "import": UserError, - "help": _("UserError. Helper to raise UserError."), - }, - "hashlib": { - "import": hashlib, - "help": _( - "Python 'hashlib' library. " - "Documentation. " - "Available methods: 'sha1', 'sha224', " - "'sha256', 'sha384'," - " 'sha512', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', " - "'shake_128', 'shake_256'," - " 'blake2b', 'blake2s', 'md5', 'new'" - ), - }, - "hmac": { - "import": hmac, - "help": _( - "Python 'hmac' library. " - "Documentation. " - "Use 'new' to create HMAC objects. " - "Available methods on the HMAC *object*: 'update', 'copy'," - " 'digest', 'hexdigest'. " - " Module-level function: 'compare_digest'." - ), - }, - "tldextract": { - "import": tldextract, - "help": _( - "Python 'tldextract' library. Use " - "tldextract.extract() to parse domains. " - "Check tldextract for more information." - ), - }, - "dns": { - "import": dns, - "help": _( - "Python 'dnspython' library. " - "Documentation." - "
    • dns.resolver: " - "wrapped dnspython. Use " - 'dns.resolver.resolve(hostname, "A") for ' - "DNS lookups.
    • " - "
    • dns.reversename: wrapped dnspython. " - 'Use dns.reversename.from_address("8.8.8.8")' - " to build and reverse PTR records.
    • " - "
    • dns.exception: wrapped dnspython. " - "Catch " - "dns.exception.DNSException to handle " - "DNS-related errors.
    • " - "
    " - ), - }, - } - custom_python_libraries = self._custom_python_libraries() - for libraries in custom_python_libraries.values(): - python_libraries.update(libraries) - return python_libraries - - def _get_python_command_odoo_objects(self, server=None): - """ - This method is used to import Odoo objects. - Because Odoo objects can be records, this method is not cached. - Use this method to import Odoo objects that are not cached. - If you need to import some static objects, use the - `_get_python_command_libraries` method instead. - - Args: - server: Server to get the Odoo objects for. - - Returns: - dict: Available Odoo objects: - {"": { - "import": , - "help": - }} - """ - return { - "uid": {"import": self._uid, "help": _("Current Odoo user ID")}, - "user": {"import": self.env.user, "help": _("Current Odoo user")}, - "env": {"import": self.env, "help": _("Odoo Environment")}, - "server": { - "import": server, - "help": _("Current Cetmix Tower server this command is running on"), - }, - "tower": { - "import": self.env["cetmix.tower"], - "help": _( - "Cetmix Tower " - "helper class shortcut" - ), - }, - "tower_servers": { - "import": self.env["cx.tower.server"], - "help": _("A helper shortcut to env['cx.tower.server']"), - }, - "tower_commands": { - "import": self.env["cx.tower.command"], - "help": _("A helper shortcut to env['cx.tower.command']"), - }, - "tower_plans": { - "import": self.env["cx.tower.plan"], - "help": _("A helper shortcut to env['cx.tower.plan']"), - }, - } - - def _custom_python_libraries(self): - """ - This function is designed to be used in custom modules - extending Cetmix Tower to add custom python libraries - to the evaluation context. - - Returns: - Dict: Custom python libraries. - - The following format is used: - { - : {"": { - "import": , - "help": - } - } - - Where: - - Odoo module technical name. - is the name of the library how it will be used in the code. - is the library to import. - is the help text for the library shown in the "Help" tab. - - Example: - - ```python - # Custom module extending Cetmix Tower - custom_python_libraries = super()._custom_python_libraries() - custom_python_libraries.update({ - "cetmix_tower_aws": { - "boto3": { - "import": boto3, - "help": "Python 'boto3' library. " - "Documentation." - }, - "custom_library_name": { - "import": custom_library_import, - "help": "Custom library help text" - } - } - }) - return custom_python_libraries - - ``` - """ - return {} - - def _get_python_command_eval_context(self, server=None, **kwargs): - """ - Get the evaluation context for the python command. - This method is used to get the evaluation context for the python command. - - Args: - server: Server to get the evaluation context for. - - Returns: - dict: Evaluation context for the python command. - """ - - # Get the Odoo objects first - imports = self._get_python_command_odoo_objects(server=server) - - # Update with the libraries - imports.update(self._get_python_command_libraries()) - eval_context = {key: value["import"] for key, value in imports.items()} - - eval_context["custom_values"] = kwargs.get("variable_values", {}) - return eval_context diff --git a/addons/cetmix_tower_server/models/cx_tower_command_log.py b/addons/cetmix_tower_server/models/cx_tower_command_log.py deleted file mode 100644 index a70db37..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_command_log.py +++ /dev/null @@ -1,369 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import logging - -from ansi2html import Ansi2HTMLConverter - -from odoo import _, api, fields, models - -from .constants import COMMAND_STOPPED, GENERAL_ERROR - -html_converter = Ansi2HTMLConverter(inline=True) -_logger = logging.getLogger(__name__) - - -class CxTowerCommandLog(models.Model): - """Command execution log""" - - _name = "cx.tower.command.log" - _description = "Cetmix Tower Command Log" - _order = "start_date desc, id desc" - - active = fields.Boolean(default=True) - name = fields.Char(compute="_compute_name", store=True) - label = fields.Char( - help="Custom label. Can be used for search/tracking", - index="trigram", - unaccent=False, - ) - server_id = fields.Many2one( - comodel_name="cx.tower.server", required=True, index=True, ondelete="cascade" - ) - - # -- Time - start_date = fields.Datetime(string="Started") - finish_date = fields.Datetime(string="Finished") - duration = fields.Float( - help="Time consumed for execution, seconds", - compute="_compute_duration", - store=True, - ) - duration_current = fields.Float( - string="Duration, sec", - compute="_compute_duration_current", - compute_sudo=True, - help="For how long a flight plan is already running", - ) - # -- Command - is_running = fields.Boolean( - help="Command is being executed right now", - compute="_compute_duration", - store=True, - ) - command_id = fields.Many2one( - comodel_name="cx.tower.command", required=True, index=True, ondelete="restrict" - ) - access_level = fields.Selection( - related="command_id.access_level", - readonly=True, - store=True, - index=True, - ) - - command_action = fields.Selection(related="command_id.action", store=True) - path = fields.Char(string="Execution Path", help="Where command was executed") - code = fields.Text(string="Command Code", help="Command code that was executed") - command_status = fields.Integer( - string="Exit Code", - help="0 if command finished successfully.\n" - "-100 general error,\n" - "-101 not found,\n" - "-201 another instance of this command is running,\n" - "-202 no runner found for the command action,\n" - "-203 Python code execution failed,\n" - "-205 plan line condition check failed,\n" - "-206 command timed out,\n" - "-207 command is not compatible with server,\n" - "-208 command is stopped by user,\n" - "503 if SSH connection error occurred", - ) - command_response = fields.Text(string="Response") - command_error = fields.Text(string="Error") - command_result_html = fields.Html( - compute="_compute_command_result_html", - help="Result converted to HTML. Used for SSH commands.", - ) - use_sudo = fields.Selection( - string="Use sudo", - selection=[("n", "Without password"), ("p", "With password")], - help="Run commands using 'sudo'", - ) - condition = fields.Char( - readonly=True, - ) - is_skipped = fields.Boolean( - readonly=True, - ) - - # -- Flight Plan - plan_log_id = fields.Many2one(comodel_name="cx.tower.plan.log", ondelete="cascade") - triggered_plan_log_id = fields.Many2one(comodel_name="cx.tower.plan.log") - - triggered_plan_command_log_ids = fields.One2many( - comodel_name="cx.tower.command.log", - inverse_name="plan_log_id", - related="triggered_plan_log_id.command_log_ids", - readonly=True, - string="Triggered Flight Plan Commands", - ) - scheduled_task_id = fields.Many2one( - "cx.tower.scheduled.task", - ondelete="set null", - help="Scheduled task that triggered this command", - ) - variable_values = fields.Json( - default={}, - help="Custom variable values passed to the command", - ) - - @api.depends("name", "command_id.name") - def _compute_name(self): - for rec in self: - rec.name = ": ".join((rec.server_id.name, rec.command_id.name)) # type: ignore - - @api.depends("start_date", "finish_date") - def _compute_duration(self): - for command_log in self: - if not command_log.start_date: - command_log.is_running = False - continue - if not command_log.finish_date: - command_log.is_running = True - continue - duration = ( - command_log.finish_date - command_log.start_date - ).total_seconds() - command_log.update( - { - "duration": duration, - "is_running": False, - } - ) - - @api.depends("is_running") - def _compute_duration_current(self): - """Shows relative time between now() and start time for running commands, - and computed duration for finished ones. - """ - now = fields.Datetime.now() - for command_log in self: - if command_log.is_running: - command_log.duration_current = ( - now - command_log.start_date - ).total_seconds() - else: - command_log.duration_current = command_log.duration - - @api.depends("command_response", "command_error") - def _compute_command_result_html(self): - for command_log in self: - command_result = command_log.command_response or command_log.command_error - if command_result: - try: - command_log.command_result_html = html_converter.convert( - command_result - ) - except Exception as e: - _logger.error("Error converting command response to HTML: %s", e) - command_log.command_result_html = _( - "

    Error converting command" - " response to HTML: %(error)s

    ", - error=e, - ) - else: - command_log.command_result_html = False - - def start(self, server_id, command_id, start_date=None, **kwargs): - """Creates initial log record when command is started - - Args: - server_id (int) id of the server. - command_id (int) id of the command. - start_date (datetime) command start date time. - **kwargs (dict): optional values - Returns: - (cx.tower.command.log()) new command log record or False - """ - vals = { - "server_id": server_id, - "command_id": command_id, - "start_date": start_date if start_date else fields.Datetime.now(), - } - # Apply kwargs - vals.update(kwargs) - log_record = self.sudo().create(vals) - return log_record - - def stop(self): - """ - Stop the command execution. - """ - user_name = self.env.user.name - for log in self: - if not log.is_running: - continue - - log.finish( - status=COMMAND_STOPPED, - error=_("Stopped by user %(user)s", user=user_name), - ) - - # Ensure flight plan log is stopped too - if log.plan_log_id and log.plan_log_id.is_running: - log.plan_log_id.stop() - - def finish( - self, finish_date=None, status=None, response=None, error=None, **kwargs - ): - """Save final command result when command is finished. - This method can be called for multiple command logs at once. - - Args: - finish_date (datetime) command finish date time. - status (int, optional): command execution status. Defaults to None. - response (Char, optional): Command response. Defaults to None. - error (Char, optional): Command error. Defaults to None. - **kwargs (dict): optional values - """ - self_with_sudo = self.sudo() - - # Duration - now = fields.Datetime.now() - date_finish = finish_date if finish_date else now - - vals = { - "finish_date": date_finish, - "command_status": GENERAL_ERROR if status is None else status, - "command_response": response, - "command_error": error, - } - - # Apply kwargs and write - vals.update(kwargs) - self_with_sudo.write(vals) - - # Trigger post finish hook - for command_log in self_with_sudo: - command_log._command_finished() - - def record( - self, - server_id, - command_id, - start_date=None, - finish_date=None, - status=0, - response=None, - error=None, - **kwargs, - ): - """Record completed command directly without using start/stop - - Args: - server_id (int) id of the server. - command_id (int) id of the command. - start_date (datetime) command start date time. - finish_date (datetime) command finish date time. - status (int, optional): command execution status. Defaults to 0. - response (list, optional): SSH response. Defaults to None. - error (list, optional): SSH error. Defaults to None. - **kwargs (dict): values to store - Returns: - (cx.tower.command.log()) new command log record - """ - vals = kwargs or {} - now = fields.Datetime.now() - vals.update( - { - "server_id": server_id, - "command_id": command_id, - "start_date": start_date or now, - "finish_date": finish_date or now, - "command_status": status, - "command_response": response, - "command_error": error, - } - ) - rec = self.sudo().create(vals) - rec._command_finished() - return rec - - def _command_finished(self): - """Triggered when command is finished - Inherit to implement your own hooks - - Returns: - bool: True if event was handled - """ - - self.ensure_one() - - # Do not notify if command is run from a Flight Plan. - if self.plan_log_id: # type: ignore - self.plan_log_id._plan_command_finished(self) # type: ignore - return True - - # Check if notifications are enabled - ICP_sudo = self.env["ir.config_parameter"].sudo() - notification_type_success = ICP_sudo.get_param( - "cetmix_tower_server.notification_type_success" - ) - notification_type_error = ICP_sudo.get_param( - "cetmix_tower_server.notification_type_error" - ) - - # Prepare notifications - if not notification_type_success and not notification_type_error: - return True - - # Use context timestamp to avoid timezone issues - context_timestamp = fields.Datetime.context_timestamp( - self, fields.Datetime.now() - ) - - # Action for button - action = self.env["ir.actions.act_window"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_command_log" - ) - - context = self.env.context.copy() - params = dict(context.get("params") or {}) - params["button_name"] = _("View Log") - context["params"] = params - action.update( - { - "views": [(False, "form")], - "context": context, - "res_id": self.id, - } - ) - - # Send notification - if self.command_status == 0 and notification_type_success: - # Success notification - self.create_uid.notify_success( - message=_( - "%(timestamp)s
    " "Command '%(name)s' finished successfully", - name=self.command_id.name, - timestamp=context_timestamp, - ), - title=self.server_id.name, - sticky=notification_type_success == "sticky", - action=action, - ) - - # Error notification - if self.command_status != 0 and notification_type_error: - self.create_uid.notify_danger( - message=_( - "%(timestamp)s
    " "Command '%(name)s' finished with error", - name=self.command_id.name, - timestamp=context_timestamp, - ), - title=self.server_id.name, - sticky=notification_type_error == "sticky", - action=action, - ) - - return True diff --git a/addons/cetmix_tower_server/models/cx_tower_custom_variable_value_mixin.py b/addons/cetmix_tower_server/models/cx_tower_custom_variable_value_mixin.py deleted file mode 100644 index 745b459..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_custom_variable_value_mixin.py +++ /dev/null @@ -1,52 +0,0 @@ -from odoo import api, fields, models - - -class CxTowerCustomVariableValueMixin(models.AbstractModel): - """ - Custom variable values. - """ - - _name = "cx.tower.custom.variable.value.mixin" - _description = "Custom variable values" - - variable_id = fields.Many2one( - "cx.tower.variable", - ) - variable_type = fields.Selection(related="variable_id.variable_type", readonly=True) - value_char = fields.Char( - string="Value", - compute="_compute_value_char", - readonly=False, - store=True, - help="Automatically populated from selected option. " - "Manual edits will be overwritten when option changes.", - ) - option_id = fields.Many2one( - "cx.tower.variable.option", domain="[('variable_id', '=', variable_id)]" - ) - - variable_value_id = fields.Many2one("cx.tower.variable.value") - required = fields.Boolean( - related="variable_value_id.required", - readonly=True, - store=True, - ) - - @api.depends("option_id", "variable_id", "variable_type") - def _compute_value_char(self): - """ - Compute value_char based on selected option for option-type variables. - For non-option variables, value_char is cleared to allow manual input. - """ - for rec in self: - if rec.variable_id and rec.variable_type == "o" and rec.option_id: - rec.value_char = rec.option_id.value_char - else: - rec.value_char = "" - - @api.onchange("variable_id") - def _onchange_variable_id(self): - """ - Reset option_id when variable changes. - """ - self.update({"option_id": None}) diff --git a/addons/cetmix_tower_server/models/cx_tower_file.py b/addons/cetmix_tower_server/models/cx_tower_file.py deleted file mode 100644 index ac23398..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_file.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from base64 import b64decode, b64encode - -from dateutil.relativedelta import relativedelta - -from odoo import _, api, fields, models -from odoo.exceptions import AccessError, UserError, ValidationError -from odoo.tools import exception_to_unicode - -# mapping of field names from template and field names from file -TEMPLATE_FILE_FIELD_MAPPING = { - "code": "code", - "file_name": "name", - "file_type": "file_type", - "server_dir": "server_dir", - "keep_when_deleted": "keep_when_deleted", - "auto_sync": "auto_sync", -} - -# to convert to 'relativedelta' object -INTERVAL_TYPES = { - "minutes": lambda interval: relativedelta(minutes=interval), - "hours": lambda interval: relativedelta(hours=interval), - "days": lambda interval: relativedelta(days=interval), - "weeks": lambda interval: relativedelta(days=7 * interval), - "months": lambda interval: relativedelta(months=interval), - "years": lambda interval: relativedelta(years=interval), -} - - -class CxTowerFile(models.Model): - """Files""" - - _name = "cx.tower.file" - _inherit = [ - "cx.tower.template.mixin", - "cx.tower.reference.mixin", - "mail.thread", - "mail.activity.mixin", - "cx.tower.key.mixin", - ] - _description = "Cetmix Tower File" - _order = "name" - - active = fields.Boolean(default=True) - name = fields.Char(help="File name WITHOUT path. Eg 'test.txt'") - rendered_name = fields.Char( - compute="_compute_render", - compute_sudo=True, - ) - template_id = fields.Many2one( - "cx.tower.file.template", - inverse="_inverse_template_id", - index=True, - ) - server_dir = fields.Char( - string="Directory on Server", - required=True, - default="", - help="Eg '/home/user' or '/var/log'", - ) - rendered_server_dir = fields.Char( - compute="_compute_render", - compute_sudo=True, - ) - full_server_path = fields.Char( - string="Full Path", - compute="_compute_render", - compute_sudo=True, - ) - source = fields.Selection( - [ - ("tower", "Tower"), - ("server", "Server"), - ], - help=""" - - Tower: file is pushed from Tower to server. - - Server: file is pulled from server to Tower. - """, - ) - auto_sync = fields.Boolean( - help="If enabled file will be synced automatically using cron", - default=False, - ) - # selection format: interval_number(integer)-interval_type(name of interval) - # it will be parsed as 'relativedelta' object - auto_sync_interval = fields.Selection( - selection=lambda self: self._selection_auto_sync_interval(), - ) - sync_date_next = fields.Datetime( - string="Next Sync Date", - required=True, - default=fields.Datetime.now, - help="Date and time of the next synchronisation", - ) - sync_date_last = fields.Datetime( - string="Last Sync Date", - readonly=True, - tracking=True, - help="Date and time of the latest successful synchronisation", - ) - server_response = fields.Text( - copy=False, - help="Server response received during the last operation.\n" - "Default value if no error happened is 'ok'.\n" - "Otherwise there will be a server error message logged.", - ) - server_id = fields.Many2one( - comodel_name="cx.tower.server", required=False, ondelete="cascade" - ) - code_on_server = fields.Text( - readonly=True, - help="Latest version of file content on server", - ) - rendered_code = fields.Char( - compute="_compute_render", - compute_sudo=True, - help="File content with variables rendered", - ) - keep_when_deleted = fields.Boolean( - help="File will be kept on server when deleted in Tower", - ) - file_type = fields.Selection( - selection=lambda self: self._selection_file_type(), - default=lambda self: self._default_file_type(), - required=True, - ) - file = fields.Binary( - string="Binary Content", - attachment=True, - ) - variable_ids = fields.Many2many( - comodel_name="cx.tower.variable", - relation="cx_tower_file_variable_rel", - column1="file_id", - column2="variable_id", - ) - - @classmethod - def _get_depends_fields(cls): - """ - Define dependent fields for computing `variable_ids` in file-related models. - - This implementation specifies that the fields `code`, `server_dir`, - and `name` are used to compute the variables associated with a file. - - Returns: - list: A list of field names (str) representing the dependencies. - - Example: - The following fields trigger recomputation of `variable_ids`: - - `code`: The content of the file. - - `server_dir`: The directory on the server where the file is located. - - `name`: The name of the file. - """ - return ["code", "server_dir", "name"] - - # -- Selection - def _selection_file_type(self): - """Available file types - - Returns: - List of tuples: available options. - """ - return [ - ("text", "Text"), - ("binary", "Binary"), - ] - - def _selection_auto_sync_interval(self): - """ - Selection of auto sync interval - """ - return [ - ("10-minutes", "10 min"), - ("30-minutes", "30 min"), - ("1-hours", "1 hour"), - ("2-hours", "2 hour"), - ("6-hours", "6 hour"), - ("12-hours", "12 hour"), - ("1-days", "1 day"), - ("1-weeks", "1 week"), - ("1-months", "1 month"), - ("1-years", "1 year"), - ] - - # -- Defaults - def _default_file_type(self): - """Default file type - - Returns: - Char: `file_type` field selection value - """ - return "text" - - # -- Computes - @api.depends("server_id", "template_id", "name", "server_dir", "code") - def _compute_render(self): - """ - Compute file name, directory and code - """ - for file in self: - if not file.server_id: - file.update( - { - "rendered_name": False, - "rendered_server_dir": False, - "rendered_code": False, - "full_server_path": False, - } - ) - continue - variables = list( - set( - file.get_variables_from_code(file.name) - + file.get_variables_from_code(file.server_dir) - + file.get_variables_from_code(file.code) - ) - ) - render_code_custom = file.render_code_custom - var_vals = file.server_id.get_variable_values(variables).get( - file.server_id.id - ) - - rendered_code = "" - if file.file_type == "text" and file.source == "tower": - rendered_code = ( - var_vals - and file.code - and render_code_custom(file.code, **var_vals) - or file.code - ) - rendered_name = ( - var_vals - and file.name - and render_code_custom(file.name, **var_vals) - or file.name - ) - rendered_server_dir = ( - var_vals - and file.server_dir - and render_code_custom(file.server_dir, **var_vals) - or file.server_dir - ) - file.update( - { - "rendered_name": rendered_name, - "rendered_server_dir": rendered_server_dir, - "rendered_code": rendered_code, - "full_server_path": f"{rendered_server_dir}/{rendered_name}", - } - ) - - # -- Onchange - @api.onchange("template_id") - def _onchange_template_id(self): - """ - Update file data by template values - """ - for file in self: - if file.template_id: - file.update(file._get_file_values_from_related_template()) - - @api.onchange("source") - def _onchange_source(self): - """ - Reset file template after change source - """ - self.update({"template_id": False}) - - def _inverse_template_id(self): - """ - Replace file fields values by template values - """ - for file in self: - if file.template_id: - file.write(file._get_file_values_from_related_template()) - - # -- Create/Write/Unlink - @api.model_create_multi - def create(self, vals_list): - """ - Override to sync files - """ - vals_list = [self._sanitize_values(vals) for vals in vals_list] - records = super().create(vals_list) - records._post_create_write("create") - return records - - def write(self, vals): - """ - Override to sync files from tower - """ - vals = self._sanitize_values(vals) - result = super().write(vals) - - # sync tower files after change - sync_fields = self._get_tower_sync_field_names() - files_to_sync = self.filtered( - lambda file: file.auto_sync - and file.source == "tower" - and any(field in vals for field in sync_fields) - ) - if files_to_sync: - files_to_sync._post_create_write("write") - return result - - def unlink(self): - """ - Override to delete from server tower files with - `keep_when_deleted` set to False - """ - self.filtered( - lambda file_: ( - file_.server_id - and file_.source == "tower" - and not file_.keep_when_deleted - ) - ).delete() - return super().unlink() - - # -- Actions - def action_unlink_from_template(self): - """ - Unlink file from template to make it editable - """ - self.ensure_one() - self.template_id = False - - def action_push_to_server(self): - """ - Push the file to server - """ - server_files = self.filtered(lambda file_: file_.source == "server") - if server_files: - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("Failure"), - "message": _( - "Unable to upload file '%(f)s'.\n" - "Upload operation is not supported for 'server' type files.", - f=server_files[0].rendered_name, - ), - "sticky": False, - }, - } - self.upload(raise_error=True) - single_msg = _("File uploaded!") - plural_msg = _("Files uploaded!") - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("Success"), - "message": single_msg if len(self) == 1 else plural_msg, - "sticky": False, - }, - } - - def action_pull_from_server(self): - """ - Pull file from server - """ - tower_files = self.filtered(lambda file_: file_.source == "tower") - server_files = self - tower_files - tower_files.action_get_current_server_code() - res = server_files.download(raise_error=True) - if isinstance(res, dict): - return res - - single_msg = _("File downloaded!") - plural_msg = _("Files downloaded!") - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("Success"), - "message": single_msg if len(self) == 1 else plural_msg, - "sticky": False, - }, - } - - def action_delete_from_server(self): - """ - Delete file from server - """ - server_files = self.filtered(lambda file_: file_.source == "server") - if server_files: - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("Failure"), - "message": _( - "Unable to delete file '%(f)s'.\n" - "Delete operation is not supported for 'server' type files.", - f=server_files[0].rendered_name, - ), - "sticky": False, - }, - } - self.delete(raise_error=True) - single_msg = _("File deleted!") - plural_msg = _("Files deleted!") - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("Success"), - "message": single_msg if len(self) == 1 else plural_msg, - "sticky": False, - }, - } - - def action_get_current_server_code(self): - """ - Get actual file code from server - """ - for file in self: - if file.source != "tower": - raise UserError( - _( - "File %(f)s is not 'tower' type. " - "This operation is supported for 'tower' " - "files only", - f=file.name, - ) - ) - - # Calling `_process` directly to get server version of a `tower` file - res = self.with_context(is_server_code_version_process=True)._process( - "download" - ) - # Type check because _process method could return - # a display_notification action dict - if isinstance(res, dict): - return res - file.code_on_server = res - - # -- Business logic - def _post_create_write(self, op_type="write"): - """Helper function that is called after file creation or update. - Use this function to implement custom hooks. - - Args: - op_type (str, optional): Operation type. Defaults to "write". - Possible options: - - "create" - - "write" - """ - - # Pull all `auto_sync` server files - server_files_to_sync = self.filtered( - lambda file: file.auto_sync and file.source == "server" - ) - if server_files_to_sync: - server_files_to_sync.action_pull_from_server() - - # Push all `auto_sync` tower files - tower_files_to_sync = self.filtered( - lambda file: file.auto_sync and file.source == "tower" - ) - if tower_files_to_sync: - tower_files_to_sync.action_push_to_server() - - def _get_file_values_from_related_template(self): - """ - Return file values from related template - """ - self.ensure_one() - if not self.template_id: - return {} - - values = self.template_id.read(list(TEMPLATE_FILE_FIELD_MAPPING), load=False)[0] - if ( - self.env.context.get("is_custom_server_dir") - and self.server_dir - and "server_dir" in values - ): - del values["server_dir"] - - return { - key: values[name] - for name, key in TEMPLATE_FILE_FIELD_MAPPING.items() - if name in values - } - - @api.model - def _sanitize_values(self, values): - """ - Check the values and reformat if necessary - """ - if "server_dir" in values: - server_dir = values["server_dir"].strip() - if server_dir.endswith("/") and server_dir != "/": - server_dir = server_dir[:-1] - values.update( - { - "server_dir": server_dir, - } - ) - return values - - def download(self, raise_error=False): - """Wrapper function for file download. - Use it for custom hooks implementation. - - Args: - raise_error (bool, optional): - Will raise and exception on error if set to 'True'. - Defaults to False. - """ - return self._process("download", raise_error) - - def upload(self, raise_error=False): - """Wrapper function for file upload. - Use it for custom hooks implementation. - - Args: - raise_error (bool, optional): - Will raise and exception on error if set to 'True'. - Defaults to False. - """ - self._process("upload", raise_error) - - def delete(self, raise_error=False): - """Wrapper function for file removal. - Use it for custom hooks implementation. - - Args: - raise_error (bool, optional): - Will raise and exception on error if set to 'True'. - Defaults to False. - """ - self._process("delete", raise_error) - - def _process_download( - self, - tower_key_obj, - is_server_code_version_process=False, - ): - """ - Processing of file download. - Note: moved this functionality to a separate function from - the general `_process` method because it is already too complex. - - Args: - tower_key_obj (RecordSet): `cx.tower.key` - recordset to parse file path. - is_server_code_version_process (bool): - Flag to fetch actual file content from server - for a `tower` type file. - - Returns: - [dict|str|None]: - display_notification action dict if there was an error - during the operation. - file content if `is_server_code_version_process` is True. - None otherwise. - """ - self.ensure_one() - code = self.server_id.download_file( - tower_key_obj._parse_code(self.full_server_path), - ) - if self.file_type == "text" and b"\x00" in code: - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("Failure"), - "message": _( - "Cannot download %(f)s from server: " - "Binary content is not supported " - "for 'Text' file type", - ) - % {"f": self.rendered_name}, - "sticky": True, - }, - } - # In case server version of a 'tower' file is requested - if is_server_code_version_process: - return code - if self.file_type == "binary": - self.file = b64encode(code) - else: - self.code = code - - def _process(self, action, raise_error=False): - """Upload or download file to/from server. - Important! - This function will return a value only in case `is_server_code_version_process` - key is present in context. - This key is used to fetch actual file content from server - for a `tower` type file. - In all other cases it will update the file content and save - server response into the `server_response` field. - - - - Args: - action (Selection): Action to process. - Possible options: - - "upload": Upload file. - - "download": Download file. - - "delete": Delete file. - raise_error (bool, optional): Raise exception if there was an error - during the operation. Defaults to False. - - Raises: - UserError: In case file format doesn't match the requested operation. - Eg if trying to upload 'server' type file. - ValidationError: In case there is an error while performing - an action with a file. - - Returns: - Char: file content or False. - """ - - tower_key_obj = self.env["cx.tower.key"] - is_server_code_version_process = self.env.context.get( - "is_server_code_version_process" - ) - for file in self: - if not is_server_code_version_process and ( - (action == "download" and file.source != "server") - or (action == "upload" and file.source != "tower") - or (action == "delete" and file.source != "tower") - ): - if raise_error: - raise UserError( - _( - "File %(f)s shouldn't have the '%(src)s' source " - " for the '%(act)s' action", - f=file.name, - src=file.source, - act=action, - ) - ) - return False - - if action == "delete": - try: - file.check_access_rights("unlink") - file.check_access_rule("unlink") - except AccessError as e: - if raise_error: - raise AccessError( - _( - "Due to security restrictions you are " - "not allowed to delete %(fp)s", - fp=file.full_server_path, - ) - ) from e - return False - - try: - if action == "download": - res = file._process_download( - tower_key_obj, is_server_code_version_process - ) - if res: - return res - elif action == "upload": - if file.file_type == "binary": - file_content = b64decode(file.file) - else: - file_content = tower_key_obj._parse_code(file.rendered_code) - file.server_id.upload_file( - file_content, - tower_key_obj._parse_code(file.full_server_path), - ) - elif action == "delete": - file.server_id.delete_file( - tower_key_obj._parse_code(file.full_server_path) - ) - else: - return False - file.sudo().server_response = "ok" - except Exception as error: - if raise_error: - raise ValidationError( - _( - "Cannot pull %(f)s from server: %(err)s", - f=file.rendered_name, - err=exception_to_unicode(error), - ) - ) from error - file.server_response = repr(error) - - if not is_server_code_version_process: - self._update_file_sync_date(fields.Datetime.now()) - - @api.model - def _get_tower_sync_field_names(self): - """ - Return the list of field names to start synchronization - after changing these fields - """ - return ["name", "server_dir", "code"] - - @api.model - def _run_auto_pull_files(self): - """ - Run auto sync files - """ - now = fields.Datetime.now() - files = self.search( - [ - ("source", "=", "server"), - ("auto_sync", "=", True), - ("sync_date_next", "<=", now), - ] - ) - files.download(raise_error=False) - - def _update_file_sync_date(self, last_sync_date): - """ - Compute and update next date of sync - """ - for file in self: - vals = {} - if file.source == "server" and file.auto_sync: - interval, interval_type = file.auto_sync_interval.split("-") - vals.update( - { - "sync_date_next": last_sync_date - + INTERVAL_TYPES[interval_type](int(interval)) - } - ) - if file.server_response == "ok": - vals.update({"sync_date_last": last_sync_date}) - file.sudo().write(vals) - - # Check cx.tower.reference.mixin for the function documentation - def _get_pre_populated_model_data(self): - res = super()._get_pre_populated_model_data() - res.update({"cx.tower.file": ["cx.tower.server", "server_id"]}) - return res diff --git a/addons/cetmix_tower_server/models/cx_tower_file_template.py b/addons/cetmix_tower_server/models/cx_tower_file_template.py deleted file mode 100644 index 94b147a..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_file_template.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright (C) 2024 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError - -from .cx_tower_file import TEMPLATE_FILE_FIELD_MAPPING - - -class CxTowerFileTemplate(models.Model): - """File template to manage multiple files at once""" - - _name = "cx.tower.file.template" - _inherit = [ - "cx.tower.reference.mixin", - "cx.tower.key.mixin", - "cx.tower.template.mixin", - "cx.tower.access.role.mixin", - "cx.tower.tag.mixin", - ] - _description = "Cetmix Tower File Template" - _order = "name" - - active = fields.Boolean(default=True) - file_name = fields.Char( - help="Default full file name with file type for example: test.txt", - ) - code = fields.Text(string="File content") - server_dir = fields.Char(string="Directory on server") - file_ids = fields.One2many("cx.tower.file", "template_id") - file_count = fields.Integer( - "File(s)", - compute="_compute_file_count", - ) - tag_ids = fields.Many2many( - relation="cx_tower_file_template_tag_rel", - column1="file_template_id", - column2="tag_id", - ) - note = fields.Text(help="This field is used to put some notes regarding template.") - keep_when_deleted = fields.Boolean( - help="File will be kept on server when deleted in Tower", - ) - auto_sync = fields.Boolean( - help="If enabled, files created from this template will have " - "Auto Sync enabled by default. Used only with 'Tower' source.", - ) - file_type = fields.Selection( - selection=lambda self: self.env["cx.tower.file"]._selection_file_type(), - default=lambda self: self.env["cx.tower.file"]._default_file_type(), - required=True, - ) - source = fields.Selection( - [ - ("tower", "Tower"), - ("server", "Server"), - ], - required=True, - default="tower", - ) - variable_ids = fields.Many2many( - comodel_name="cx.tower.variable", - relation="cx_tower_file_template_variable_rel", - column1="file_template_id", - column2="variable_id", - ) - - # ---- Access. Add relation for mixin fields - user_ids = fields.Many2many( - relation="cx_tower_file_template_user_rel", - domain=lambda self: [ - ("groups_id", "in", [self.env.ref("cetmix_tower_server.group_manager").id]) - ], - ) - manager_ids = fields.Many2many( - relation="cx_tower_file_template_manager_rel", - ) - - @classmethod - def _get_depends_fields(cls): - """ - Define dependent fields for computing - `variable_ids` in file template-related models. - - This implementation specifies that the fields `code`, `server_dir`, - and `file_name` are used to compute the - variables associated with a file template. - - Returns: - list: A list of field names (str) representing the dependencies. - - Example: - The following fields trigger recomputation - of `variable_ids`: - - `code`: The template content for the file. - - `server_dir`: The target directory on the - server where the template is applied. - - `file_name`: The name of the generated file. - """ - return ["code", "server_dir", "file_name"] - - # -- Computes - @api.depends("file_ids") - def _compute_file_count(self): - """ - Compute total template files - """ - for template in self: - template.file_count = len(template.file_ids) - - # -- Create/Write/Unlink - def write(self, vals): - """ - Override to update files related with the templates - """ - result = super().write(vals) - if any([field_ in vals for field_ in TEMPLATE_FILE_FIELD_MAPPING]): - for file in self.mapped("file_ids"): - file.write(file._get_file_values_from_related_template()) - return result - - # -- Actions - def action_open_files(self): - """ - Open current template files - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.cx_tower_file_action" - ) - action["domain"] = [("id", "in", self.file_ids.ids)] - return action - - # -- Business logic - def create_file(self, server, server_dir="", if_file_exists="raise"): - """ - Create a new file using the current template for the selected server. - If the same file already exists, just ignore it or raise an error based on the - parameter. - - :param server: recordset - The server (cx.tower.server) on which the file should be created. This is a - required parameter. - :param if_file_exists: str, optional - Defines the behavior if the file already exists on the server. - :param server_dir: str, optional - The directory on the server where the file should be created. If not set, - the server_dir field of the template will be used. - - :return: cx.tower.file - Returns the newly created file record (cx.tower.file) if the file was - created successfully or if_file_exists is set to "overwrite". - Returns the existing file record if the file already exists - and if_file_exists is set to "skip". - - :raises ValidationError: - If the file already exists on the server if_file_exists is set to "raise". - """ - self.ensure_one() - # Explicit guard against invalid behavior values - valid_behaviors = {"skip", "raise", "overwrite"} - if if_file_exists not in valid_behaviors: - raise ValidationError( - f"Invalid if_file_exists value: {if_file_exists}. " - f"Expected one of {valid_behaviors}." - ) - file_model = self.env["cx.tower.file"] - existing_files = file_model.search( - [ - ("server_id", "=", server.id), - ("source", "=", self.source), - ], - order="id DESC", - ) - existing_dir = server_dir or self.server_dir - - # Render the server directory and file name from the template - variables = list( - set( - self.get_variables_from_code(self.file_name) - + self.get_variables_from_code(existing_dir) - ) - ) - var_vals = server.get_variable_values(variables).get(server.id) or {} - - unrendered_path = ( - f"{existing_dir}/{self.file_name}" if existing_dir else self.file_name - ) - rendered_path = self.render_code_custom(unrendered_path, **var_vals) - - # Filter existing files by rendered path - existing_files = existing_files.filtered( - lambda f: f.full_server_path == rendered_path - ) - - # Filter existing files by template if it exists, otherwise take the first one - existing_file = ( - existing_files.filtered(lambda f: f.template_id == self)[:1] - or existing_files[:1] - ) - - if existing_file and if_file_exists == "skip": - return existing_file.with_context(file_creation_skipped=True) - - if existing_file and if_file_exists == "raise": - raise ValidationError(_("File already exists on server.")) - - if existing_file and if_file_exists == "overwrite": - existing_file.with_context(is_custom_server_dir=True).write( - { - "template_id": self.id, - } - ) - return existing_file - - vals = { - "name": self.file_name, - "server_id": server.id, - "server_dir": existing_dir, - "template_id": self.id, - "code": self.code, - "file_type": self.file_type, - "source": self.source, - "auto_sync": self.auto_sync, - } - - new_file = file_model.with_context(is_custom_server_dir=True).create(vals) - # Return new_file if no file exists - return new_file diff --git a/addons/cetmix_tower_server/models/cx_tower_key.py b/addons/cetmix_tower_server/models/cx_tower_key.py deleted file mode 100644 index 3ab983d..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_key.py +++ /dev/null @@ -1,414 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, fields, models - - -class CxTowerKey(models.Model): - """SSH Private key and secret storage""" - - _name = "cx.tower.key" - _description = "Cetmix Tower Key/Secret Storage" - _inherit = [ - "cx.tower.reference.mixin", - "cx.tower.access.role.mixin", - "cx.tower.vault.mixin", - ] - _order = "name" - - KEY_PREFIX = "#!cxtower" - KEY_TERMINATOR = "!#" - SECRET_FIELDS = ["secret_value"] - - key_type = fields.Selection( - selection=[ - ("k", "SSH Key"), - ("s", "Secret"), - ], - required=True, - ) - reference_code = fields.Char( - compute="_compute_reference_code", - help="Key reference for inline usage", - ) - secret_value = fields.Text( - string="SSH Private Key", - ) - value_ids = fields.One2many( - string="Values", - comodel_name="cx.tower.key.value", - inverse_name="key_id", - ) - server_ssh_ids = fields.One2many( - string="Used as SSH Key", - comodel_name="cx.tower.server", - inverse_name="ssh_key_id", - readonly=True, - help="Used as SSH key in the following servers", - ) - note = fields.Text() - - # ---- Access. Add relation for mixin fields - user_ids = fields.Many2many( - relation="cx_tower_key_user_rel", - domain=lambda self: [ - ("groups_id", "in", [self.env.ref("cetmix_tower_server.group_manager").id]) - ], - ) - manager_ids = fields.Many2many( - relation="cx_tower_key_manager_rel", - ) - - @api.depends("reference", "key_type") - def _compute_reference_code(self): - """Compute key reference - Eg '#!cxtower.secret.KEY!#' - """ - for rec in self: - if rec.reference: - key_prefix = self._compose_key_prefix(rec.key_type) - if key_prefix: - rec.reference_code = f"#!cxtower.{key_prefix}.{rec.reference}!#" - else: - rec.reference_code = None - else: - rec.reference_code = None - - @api.returns("self", lambda value: value.id) - def copy(self, default=None): - """Copy key. Ensure secret value is copied. - - Args: - default (dict, optional): Default values. Defaults to None. - - Returns: - self: Copied key - """ - default = default or {} - default["secret_value"] = self._get_secret_value("secret_value") - result = super().copy(default=default) - - # Copy key values - for value in self.value_ids: - value.copy( - { - "key_id": result.id, - } - ) - - return result - - def _get_reference_pattern(self): - """ - Override mixin method - """ - return "[a-zA-Z0-9_]" - - def _compose_key_prefix(self, key_type): - """Compose key prefix based on key type. - Override to implement own key prefixes. - - - Args: - key_type (_type_): _description_ - - Raises: - ValidationError: _description_ - - Returns: - Char: key prefix - """ - if key_type == "s": - key_prefix = "secret" - else: - key_prefix = None - return key_prefix - - def _parse_code_and_return_key_values(self, code, pythonic_mode=False, **kwargs): - """Replaces key placeholders in code with the corresponding values, - returning key values. - - This function is meant to be used in the flow where key values - are needed for some follow up operations such as command log clean up. - - NB: - - key format must follow "#!cxtower.key.KEY_ID!#" pattern. - eg #!cxtower.secret.GITHUB_TOKEN!# for GITHUB_TOKEN key - Args: - code (Text): code to process - pythonic_mode (Bool): If True, all variables in kwargs are converted to - strings and wrapped in double quotes. - Default is False. - kwargs (dict): optional arguments - - Returns: - Dict(): 'code': Command text, 'key_values': List of key values - """ - - # No need to search if code is too short - if len(code) <= len(self.KEY_PREFIX) + 3 + len( - self.KEY_TERMINATOR - ): # at least one dot separator and two symbols - return {"code": code, "key_values": None} - - # Get key strings - key_strings = self._extract_key_strings(code) - - # Set key values - key_values = [] - # Replace keys with values - for key_string in key_strings: - # Replace key including key terminator - key_value = self._parse_key_string(key_string, **kwargs) - if pythonic_mode and key_value: - # save key value as string in pythonic mode - key_value = f'"{key_value}"' - # Escape newline characters to ensure the key value remains - # a valid single-line string. This prevents syntax errors - # when the string is used in contexts where unescaped - # newlines would break Python syntax or evaluation logic. - key_value = key_value.replace("\n", "\\n") - - # Save key value if not saved yet - if key_value and key_value not in key_values: - key_values.append(key_value) - - # Handle False and None values - if not key_value: - key_value = str(key_value) - - # Replace key with value - code = code.replace(key_string, key_value) - - return {"code": code, "key_values": key_values} - - def _parse_code(self, code, **kwargs): - """Replaces key placeholders in code with the corresponding values. - - Args: - code (Text): code to proceed - kwargs (dict): optional arguments - - Returns: - Text: code with key values in place and list of key values. - Use key values - """ - - return self._parse_code_and_return_key_values(code, **kwargs)["code"] - - def _extract_key_strings(self, code): - """Extract all keys from code - Args: - code (Text): description - **kwargs (dict): optional arguments - Returns: - [str]: list of key strings - """ - key_strings = [] - key_terminator_len = len(self.KEY_TERMINATOR) - index_from = 0 # initial position - - while index_from >= 0: - index_from = code.find(self.KEY_PREFIX, index_from) - if index_from >= 0: - # Key end - index_to = code.find(self.KEY_TERMINATOR, index_from) - # Extract key value only if key terminator is found - if index_to > 0: - # Extract key string including key terminator - extract_to = index_to + key_terminator_len - key_string = code[index_from:extract_to] - # Add only if not added before - if key_string not in key_strings: - key_strings.append(key_string) - # Update index from - index_from = extract_to - else: - # No terminator found, move past this occurrence of prefix - index_from += len(self.KEY_PREFIX) - else: - # No more prefixes found - break - - return key_strings - - def _parse_key_string(self, key_string, **kwargs): - """Parse key string and call resolver based on the key type. - Each key string consists of 3 parts: - - key marker: #!cxtower - - key type: e.g. "secret", "password", "login" etc - - key ID: e.g "qwerty123", "mystrongpassword" etc - - Inherit this function to implement your own parser or resolver - Args: - key_string (str): key string - **kwargs (dict) optional values - - Returns: - str: key value or None if not able to parse - """ - - key_parts = self._extract_key_parts(key_string) - if key_parts is None: - return None - - key_type, reference = key_parts - key_value = self._resolve_key(key_type, reference, **kwargs) - - return key_value - - def _extract_key_parts(self, key_string): - """Extract and validate key parts from the key string. - - Args: - key_string (str): key string - - Returns: - tuple: (key_type, reference) if valid, else None - """ - key_parts = ( - key_string.replace(" ", "").replace(self.KEY_TERMINATOR, "").split(".") - ) - - # Must be 3 parts including pre! - if len(key_parts) == 3 and key_parts[0] == self.KEY_PREFIX: - return key_parts[1], key_parts[2] - - return None - - def _resolve_key(self, key_type, reference, **kwargs): - """Resolve key - Inherit this function to implement your own resolvers - - Args: - reference (str): key reference - **kwargs (dict) optional values - - Returns: - str: value or None if not able to parse - """ - if key_type == "secret": - return self._resolve_key_type_secret(reference, **kwargs) - - def _resolve_key_type_secret(self, reference, **kwargs): - """Resolve key of type "secret". - Use this function as a custom parser example - - Args: - reference (str): key reference - **kwargs (dict) optional values - - Returns: - str: value or False if not able to parse - """ - if not reference: - return - - # Compose domain used to fetch keys - # - # Keys are checked in the following order: - # 1. Partner and Server specific - # 2. Server specific - # 3. Partner specific - # 4. General (no server or partner specified) - server_id = kwargs.get("server_id") - partner_id = kwargs.get("partner_id") - - # Fetch key - key = self.sudo().search([("reference", "=", reference)], limit=1) - if not key: - return - - # Check if key has custom values - key_values = key.value_ids - key_value = None - - # 1. Server and Partner specific key first - if key_values and server_id and partner_id: - filtered_key_values = key_values.filtered( - lambda k: k.server_id.id == server_id and k.partner_id.id == partner_id - ) - if filtered_key_values: - key_value = filtered_key_values[0] - - # 2. Server specific key first - if not key_value and key_values and server_id: - filtered_key_values = key_values.filtered( - lambda k: k.server_id.id == server_id and not k.partner_id - ) - if filtered_key_values: - key_value = filtered_key_values[0] - - # 3. Partner specific key next - if not key_value and key_values and partner_id: - filtered_key_values = key_values.filtered( - lambda k: k.partner_id.id == partner_id and not k.server_id - ) - if filtered_key_values: - key_value = filtered_key_values[0] - - # 4. General key next - if not key_value and key_values: - filtered_key_values = key_values.filtered( - lambda k: not k.partner_id and not k.server_id - ) - if filtered_key_values: - key_value = filtered_key_values[0] - - if key_value: - return key_value._get_secret_value("secret_value") - - def _replace_with_spoiler(self, code, key_values): - """Helper function that replaces clean text keys in code with spoiler. - Eg - 'Code with passwordX and passwordY` will look like: - 'Code with *** and ***' - - Important: this function doesn't parse keys by itself. - You need to get and provide key values yourself. - - Args: - code (Text): code to clean - key_values (List): secret values to be cleaned from code - - Returns: - Text: cleaned code - """ - - if not key_values: - return code - - # Replace keys with values - for key_value in key_values: - # If key_value includes quotes, remove them for the replacement - key_value = key_value.strip('"') - # If key_value contains an escaped line break replace then remove escaping - key_value = key_value.replace("\\n", "\n") - # Replace key including key terminator - code = code.replace(key_value, self.SECRET_VALUE_PLACEHOLDER) - - return code - - def _set_secret_values(self, vals): - """Set secret value. - Override this method in case you need - to implement custom key storages. - - Args: - vals (dict): Dictionary of field names to secret values - """ - self.ensure_one() - if self.key_type == "s": - # Set general value or create new one if not exists - general_value = self.value_ids.filtered( - lambda x: not x.server_id and not x.partner_id - ) - if general_value: - general_value._set_secret_values(vals) - else: - create_vals = {"key_id": self.id} - create_vals.update(vals) - self.value_ids.create(create_vals) - - elif self.key_type == "k": - return super()._set_secret_values(vals) diff --git a/addons/cetmix_tower_server/models/cx_tower_key_mixin.py b/addons/cetmix_tower_server/models/cx_tower_key_mixin.py deleted file mode 100644 index e10800f..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_key_mixin.py +++ /dev/null @@ -1,70 +0,0 @@ -from odoo import api, fields, models - - -class CxTowerKeyMixin(models.AbstractModel): - """Mixin for managing secrets and SSH keys""" - - _name = "cx.tower.key.mixin" - _description = "Cetmix Tower Key/Secret Mixin" - - secret_ids = fields.Many2many( - comodel_name="cx.tower.key", - compute="_compute_secret_ids", - compute_sudo=True, - readonly=True, - store=True, - string="Secrets", - ) - - @api.depends("code") - def _compute_secret_ids(self): - """ - Compute the secret IDs based on the references found in the code field. - - This method updates the secret_ids Many2many field by extracting secret - references from the code field. If no code is present, the field is cleared. - It ensures updates are only triggered when there are differences between - the current and new secret IDs. - """ - for record in self: - if record.code: - new_secrets = self._extract_secret_ids(record.code) - - # This will create a recordset that contains the difference - if record.secret_ids != new_secrets: - record.secret_ids = new_secrets - else: - record.secret_ids = [(5, 0, 0)] - - @api.model - def _extract_secret_ids(self, code): - """ - Extract secret IDs based on references found in the given `code`. - - Args: - code: Text containing potential secret references. - - Returns: - list: List of secret IDs corresponding to the references in `code`. - """ - key_model = self.env["cx.tower.key"] - key_strings = key_model._extract_key_strings(code) - - key_refs = [] - for key_string in key_strings: - key_parts = key_model._extract_key_parts(key_string) - if key_parts: - key_refs.append(key_parts[1]) - - return key_model.search(self._compose_secret_search_domain(key_refs)) - - def _compose_secret_search_domain(self, key_refs): - """Compose domain for searching secrets by references. - - Args: - key_refs (List[str]): List of secret references. - - Returns: - List: final domain for searching secrets. - """ - return [("reference", "in", key_refs)] diff --git a/addons/cetmix_tower_server/models/cx_tower_key_value.py b/addons/cetmix_tower_server/models/cx_tower_key_value.py deleted file mode 100644 index faa4115..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_key_value.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError - - -class CxTowerKeyValue(models.Model): - """Secret value storage""" - - _name = "cx.tower.key.value" - _inherit = [ - "cx.tower.reference.mixin", - "cx.tower.vault.mixin", - ] - _description = "Cetmix Tower Secret Value Storage" - - SECRET_FIELDS = ["secret_value"] - - name = fields.Char(related="key_id.name", readonly=False) - key_id = fields.Many2one( - comodel_name="cx.tower.key", - string="Key", - required=True, - ondelete="cascade", - domain="[('key_type', '=', 's')]", - ) - server_id = fields.Many2one( - comodel_name="cx.tower.server", - ondelete="cascade", - help="Server to which the key belongs", - ) - partner_id = fields.Many2one( - comodel_name="res.partner", - ondelete="cascade", - help="Partner to which the key belongs", - ) - is_global = fields.Boolean( - string="Global", - compute="_compute_is_global", - help="This value is applicable to all servers and partners", - ) - secret_value = fields.Text() - - @api.depends("server_id", "partner_id") - def _compute_is_global(self): - for record in self: - record.is_global = not record.server_id and not record.partner_id - - @api.constrains("key_id", "server_id", "partner_id") - def _check_key_id(self): - for rec in self: - if not rec.key_id: - continue - # Only keys of type 'secret' can have custom secret values - if rec.key_id.key_type != "s": - raise ValidationError( - _( - "Custom secret values can be defined" - " only for key type 'secret'" - ) - ) - # Only one global secret value can be defined for a key - global_values = rec.key_id.value_ids.filtered( - lambda x, rec=rec: not x.server_id and not x.partner_id - ) - if len(global_values) > 1: - raise ValidationError( - _("Only one global secret value can be defined for a key") - ) - # Only one secret value can be defined for a server and partner - server_partner_values = rec.key_id.value_ids.filtered( - lambda x, rec=rec: x.server_id == rec.server_id - and x.partner_id == rec.partner_id - ) - if len(server_partner_values) > 1: - raise ValidationError( - _( - "Only one secret value can be defined" - " for a server and partner" - ) - ) - # Only one secret value can be defined for a server - server_values = rec.key_id.value_ids.filtered( - lambda x, rec=rec: x.server_id == rec.server_id and not x.partner_id - ) - if len(server_values) > 1: - raise ValidationError( - _("Only one secret value can be defined for a server") - ) - # Only one secret value can be defined for a partner - partner_values = rec.key_id.value_ids.filtered( - lambda x, rec=rec: x.partner_id == rec.partner_id and not x.server_id - ) - if len(partner_values) > 1: - raise ValidationError( - _("Only one secret value can be defined for a partner") - ) - - @api.returns("self", lambda value: value.id) - def copy(self, default=None): - """Copy key value. Ensure secret value is copied. - - Args: - default (dict, optional): Default values. Defaults to None. - - Returns: - self: Copied key value - """ - default = default or {} - default["secret_value"] = self._get_secret_value("secret_value") - return super().copy(default=default) diff --git a/addons/cetmix_tower_server/models/cx_tower_os.py b/addons/cetmix_tower_server/models/cx_tower_os.py deleted file mode 100644 index f98dc4c..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_os.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class CxTowerOs(models.Model): - """Operating System""" - - _name = "cx.tower.os" - _inherit = [ - "cx.tower.reference.mixin", - ] - _description = "Cetmix Tower Operating System" - _order = "name" - - color = fields.Integer(help="For better visualization in views") - parent_id = fields.Many2one(string="Previous Version", comodel_name="cx.tower.os") diff --git a/addons/cetmix_tower_server/models/cx_tower_plan.py b/addons/cetmix_tower_server/models/cx_tower_plan.py deleted file mode 100644 index e02accc..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_plan.py +++ /dev/null @@ -1,373 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from operator import indexOf - -from odoo import _, api, fields, models -from odoo.tools.safe_eval import expr_eval - -from .constants import ( - ANOTHER_PLAN_RUNNING, - PLAN_LINE_CONDITION_CHECK_FAILED, - PLAN_LINE_NOT_ASSIGNED, - PLAN_NOT_ASSIGNED, - PLAN_NOT_COMPATIBLE_WITH_SERVER, -) - - -class CxTowerPlan(models.Model): - """Cetmix Tower flight plan""" - - _name = "cx.tower.plan" - _description = "Cetmix Tower Flight Plan" - _inherit = [ - "cx.tower.reference.mixin", - "cx.tower.access.mixin", - "cx.tower.access.role.mixin", - "cx.tower.tag.mixin", - ] - _order = "name asc" - - active = fields.Boolean(default=True) - allow_parallel_run = fields.Boolean( - help="If enabled, multiple instances of the same flight plan " - "can be run on the same server at the same time.\n" - "Otherwise, ANOTHER_PLAN_RUNNING status will be returned if another" - " instance of the same flight plan is already running" - ) - - color = fields.Integer(help="For better visualization in views") - server_ids = fields.Many2many(string="Servers", comodel_name="cx.tower.server") - tag_ids = fields.Many2many( - relation="cx_tower_plan_tag_rel", - column1="plan_id", - column2="tag_id", - ) - line_ids = fields.One2many( - string="Lines", - comodel_name="cx.tower.plan.line", - inverse_name="plan_id", - auto_join=True, - copy=True, - ) - command_ids = fields.Many2many( - string="Commands", - comodel_name="cx.tower.command", - relation="cx_tower_command_flight_plan_used_id_rel", - column1="plan_id", - column2="command_id", - help="Commands used in this flight plan", - compute="_compute_command_ids", - store=True, - ) - note = fields.Text() - on_error_action = fields.Selection( - string="On Error", - selection=[ - ("e", "Exit with command exit code"), - ("ec", "Exit with custom exit code"), - ("n", "Run next command"), - ], - required=True, - default="e", - help="This action will be triggered on error " - "if no command action can be applied", - ) - custom_exit_code = fields.Integer( - help="Will be used instead of the command exit code" - ) - - access_level_warn_msg = fields.Text( - compute="_compute_command_access_level", - compute_sudo=True, - ) - - # ---- Access. Add relation for mixin fields - user_ids = fields.Many2many( - relation="cx_tower_plan_user_rel", - ) - manager_ids = fields.Many2many( - relation="cx_tower_plan_manager_rel", - ) - - @api.depends("line_ids.command_id.access_level", "access_level") - def _compute_command_access_level(self): - """Check if the access level of a command in the plan - is higher than the plan's access level""" - for record in self: - commands = record.mapped("line_ids").mapped("command_id") - # Retrieve all commands associated with the flight plan - commands_with_higher_access = commands.filtered( - lambda c, access_level=record.access_level: c.access_level - > access_level - ) - if commands_with_higher_access: - command_names = ", ".join(commands_with_higher_access.mapped("name")) - record.access_level_warn_msg = _( - "The access level of command(s) '%(command_names)s' included in the" - " current Flight plan is higher than the access level of the" - " Flight plan itself. Please ensure that you want to allow" - " those commands to be run anyway.", - command_names=command_names, - ) - else: - record.access_level_warn_msg = False - - @api.depends("line_ids", "line_ids.command_id") - def _compute_command_ids(self): - """Compute command ids""" - for plan in self: - plan.command_ids = [(6, 0, plan.line_ids.mapped("command_id").ids)] - - def action_open_plan_logs(self): - """ - Open current flight plan log records - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_plan_log" - ) - action["domain"] = [("plan_id", "=", self.id)] - return action - - def _get_dependent_model_relation_fields(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_dependent_model_relation_fields() - return res + ["line_ids"] - - def _is_plan_incompatible_with_server(self, server): - """ - Check if the flight plan is compatible with the server. - Note: this function uses the inverse logic to simplify the checks. - - Args: - server (cx.tower.server()): Server object - - Returns: - Char or False: Incompatible reason or False if compatible - """ - - # Check if the flight plan is compatible with the server - if not self.server_ids: - return False - if server.id not in self.server_ids.ids: - return _("Flight plan is not compatible with the server") - - # Check if the flight plan commands are compatible with the server - for command in self.command_ids: - # Check the entire command first - if not command._check_server_compatibility(server): - return _( - "Command %(command_name)s is not compatible with the server", - command_name=command.name, - ) # pylint: disable=no-member - - # Check if the nested flight plan is compatible with the server - if command.action == "plan": - plan_check_result = ( - command.flight_plan_id._is_plan_incompatible_with_server(server) - ) - if plan_check_result: - return plan_check_result - - return False - - def _get_post_create_fields(self): - res = super()._get_post_create_fields() - return res + ["line_ids"] - - def _run_single(self, server, **kwargs): - """Run single Flight Plan on a single server - - Args: - server (cx.tower.server()): Server object - kwargs (dict): Optional arguments - Following are supported but not limited to: - - "plan_log": {values passed to flightplan logger} - - "log": {values passed to logger} - - "key": {values passed to key parser} - - "variable_values", dict(): custom variable values - in the format of `{variable_reference: variable_value}` - eg `{'odoo_version': '16.0'}` - Will be applied only if user has write access to the server. - - Returns: - log_record (cx.tower.plan.log()): plan log record - """ - - self.ensure_one() - # Ensure we have a single server record - server.ensure_one() - - # Check plan access before running - # This is needed to avoid possible access violations - self.check_access_rights("read") - self.check_access_rule("read") - - # Access log as root to bypass access restrictions - plan_log_obj = self.env["cx.tower.plan.log"].sudo() - - # Check if flight plan and all its commands can be run on this server - # This check is skipped if 'from_command' context key is set to True - if not self.env.context.get("from_command"): - plan_is_incompatible = self._is_plan_incompatible_with_server(server) - if plan_is_incompatible: - # Create a log record with the custom message and exit - plan_log_kwargs = kwargs.get("plan_log", {}) - plan_log_kwargs["custom_message"] = plan_is_incompatible - kwargs["plan_log"] = plan_log_kwargs - plan_log = plan_log_obj.record( - server=server, - plan=self, - status=PLAN_NOT_COMPATIBLE_WITH_SERVER, - **kwargs, - ) - return plan_log - - # Check if the same plan is being run on this server right now - if not self.allow_parallel_run or self.env.context.get( - "prevent_plan_recursion" - ): - running_count = plan_log_obj.search_count( - [ - ("server_id", "=", server.id), - ("plan_id", "=", self.id), # pylint: disable=no-member - ("is_running", "=", True), - ] - ) - if running_count > 0: - plan_log = plan_log_obj.record( - server=server, plan=self, status=ANOTHER_PLAN_RUNNING, **kwargs - ) - return plan_log - - # Start Flight Plan and return the log record - return plan_log_obj.start(server, self, fields.Datetime.now(), **kwargs) - - def _get_next_action_values(self, command_log): - """Get next action values based of previous command result: - - - Action to proceed - - Exit code - - Next line of the plan if next line should be run - - Args: - command_log (cx.tower.command.log()): Command log record - - Returns: - action, exit_code, next_line (Selection, Integer, cx.tower.plan.line()) - - """ - # Iterate all actions and return the first matching one. - # If no action is found return the default plan values - # If the line is the last one return last command exit code - - if not command_log.plan_log_id: # Exit with custom code "Plan not found" - return "ec", PLAN_NOT_ASSIGNED, None - - current_line = command_log.plan_log_id.plan_line_executed_id - if not current_line: - return "ec", PLAN_LINE_NOT_ASSIGNED, None - - # Default values - exit_code = command_log.command_status - server = command_log.server_id - - # Check line condition - variable_values = ( - command_log.variable_values or command_log.plan_log_id.variable_values or {} - ) - if not current_line._is_executable_line( - server, variable_values=variable_values - ): - # Immediately return to the next line if condition fails - return self._get_next_action_state( - "n", PLAN_LINE_CONDITION_CHECK_FAILED, current_line - ) - - # Check plan action lines - for action_line in current_line.action_ids: - conditional_expression = ( - f"{exit_code} {action_line.condition} {action_line.value_char}" - ) - # Evaluate expression using safe_eval - if expr_eval(conditional_expression): - action = action_line.action - # Use custom exit code if action requires it - if action == "ec" and action_line.custom_exit_code: - exit_code = action_line.custom_exit_code - - # Apply action-defined values into the variable values context - for variable_value in action_line.variable_value_ids: - ref = variable_value.variable_id.reference - variable_values[ref] = variable_value.value_char - - # Persist the updated custom values only in logs - # so they remain available within the current flight plan context - updated_values = dict(variable_values) - command_log.variable_values = updated_values - if command_log.plan_log_id: - command_log.plan_log_id.variable_values = updated_values - - return self._get_next_action_state(action, exit_code, current_line) - - # If no action matched, fallback to default ones - return self._get_next_action_state(None, exit_code, current_line) - - def _get_next_action_state(self, action, exit_code, current_line): - """ - Determine the next action, exit code, and next line based on the current state. - """ - lines = current_line.plan_id.line_ids - is_last_line = current_line == lines[-1] - - # If no conditions were met fallback to default ones - if not action: - action = "n" if exit_code == 0 else current_line.plan_id.on_error_action - - # Exit with custom code - if action == "ec": - exit_code = current_line.plan_id.custom_exit_code - - # Determine the next line if current is not the last one - next_line = None - if action == "n" and not is_last_line: - next_line = lines[indexOf(lines, current_line) + 1] - - if is_last_line: - action = "e" - - return action, exit_code, next_line - - def _run_next_action(self, command_log): - """Run next action based on the command result - - Args: - command_log (cx.tower.command.log()): Command log record - """ - self.ensure_one() - action, exit_code, plan_line = self._get_next_action_values(command_log) - plan_log = command_log.plan_log_id - - # Update log message - if exit_code == PLAN_LINE_CONDITION_CHECK_FAILED: - # save log exit code as success - exit_code = 0 - - # Run next line - if action == "n" and plan_line: - server = command_log.server_id - variable_values = command_log.variable_values or plan_log.variable_values - if plan_line._is_executable_line(server, variable_values=variable_values): - plan_line._run(server, plan_log, variable_values=variable_values) - else: - plan_line._skip( - server, - plan_log, - log={"variable_values": dict(variable_values or {})}, - ) - - # Exit - if action in ["e", "ec"]: - plan_log.finish(exit_code) - - # NB: we are not putting any fallback here in case - # someone needs to inherit and extend this function diff --git a/addons/cetmix_tower_server/models/cx_tower_plan_line.py b/addons/cetmix_tower_server/models/cx_tower_plan_line.py deleted file mode 100644 index 05b0a23..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_plan_line.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging - -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError -from odoo.tools.safe_eval import safe_eval - -from .constants import PLAN_LINE_CONDITION_CHECK_FAILED - -_logger = logging.getLogger(__name__) - - -class CxTowerPlanLine(models.Model): - """Flight Plan Line""" - - _name = "cx.tower.plan.line" - _inherit = [ - "cx.tower.reference.mixin", - ] - _order = "sequence, plan_id" - _description = "Cetmix Tower Flight Plan Line" - - active = fields.Boolean(related="plan_id.active", readonly=True) - sequence = fields.Integer(default=10) - name = fields.Char(related="command_id.name", readonly=True) - plan_id = fields.Many2one( - string="Flight Plan", - comodel_name="cx.tower.plan", - auto_join=True, - ondelete="cascade", - ) - action = fields.Selection( - selection=lambda self: self.command_id._selection_action(), - compute="_compute_action", - required=True, - readonly=False, - ) - command_id = fields.Many2one( - comodel_name="cx.tower.command", - required=True, - ondelete="restrict", - domain="[('action', '=', action)]", - ) - note = fields.Text(related="command_id.note", readonly=True) - path = fields.Char( - help="Location where command will be executed. Overrides command default path. " - "You can use {{ variables }} in path", - ) - - use_sudo = fields.Boolean( - help="Will use sudo based on server settings." - "If no sudo is configured will run without sudo" - ) - action_ids = fields.One2many( - string="Actions", - comodel_name="cx.tower.plan.line.action", - inverse_name="line_id", - auto_join=True, - copy=True, - help="Actions trigger based on command result." - " If empty next command will be executed", - ) - command_code = fields.Text( - related="command_id.code", - readonly=True, - ) - tag_ids = fields.Many2many(related="command_id.tag_ids", readonly=True) - access_level = fields.Selection( - related="plan_id.access_level", - readonly=True, - store=True, - ) - condition = fields.Char( - help="Conditions under which this Flight Plan Line " - "will be launched. e.g.: {{ odoo_version}} == '14.0'", - ) - variable_ids = fields.Many2many( - comodel_name="cx.tower.variable", - relation="cx_tower_plan_line_variable_rel", - column1="plan_line_id", - column2="variable_id", - string="Variables", - compute="_compute_variable_ids", - store=True, - ) - # -- Command related entities - plan_run_id = fields.Many2one( - comodel_name="cx.tower.plan", - related="command_id.flight_plan_id", - readonly=True, - string="Run Flight Plan", - ) - plan_run_line_ids = fields.One2many( - comodel_name="cx.tower.plan.line", - related="command_id.flight_plan_id.line_ids", - string="Flight Plan Lines", - readonly=True, - ) - file_template_id = fields.Many2one( - comodel_name="cx.tower.file.template", - related="command_id.file_template_id", - readonly=True, - ) - file_template_code = fields.Text( - string="Template Code", - related="file_template_id.code", - readonly=True, - ) - - @api.depends("condition") - def _compute_variable_ids(self): - """ - Compute variable_ids based on condition field. - """ - template_mixin_obj = self.env["cx.tower.template.mixin"] - for record in self: - record.variable_ids = template_mixin_obj._prepare_variable_commands( - ["condition"], force_record=record - ) - - def _compute_action(self): - """ - Compute action based on command. - """ - - # We set action only once, so there is no 'depends' in this function - for record in self: - if record.action: - continue - if record.command_id: - record.action = record.command_id.action - else: - record.action = False - - @api.constrains("command_id") - def _check_command_id(self): - """ - Check recursive plan line execution. - """ - for line in self: - # Check recursive plan line execution - visited_plans = set() - self._check_recursive_plan(line.command_id, visited_plans) - - @api.onchange("action") - def _inverse_action(self): - """ - Reset command when action changes. - """ - self.command_id = False - - def _check_recursive_plan(self, command, visited_plans): - """ - Recursively check if the command plan creates a cycle. - Raise a ValidationError if a cycle is detected. - """ - if command.flight_plan_id and command.action == "plan": - if command.flight_plan_id.id in visited_plans: - raise ValidationError( - _( - "Recursive plan call detected in plan %(name)s.", - name=command.flight_plan_id.name, - ) - ) - visited_plans.add(command.flight_plan_id.id) - # recursively check the lines in the plan - for line in command.flight_plan_id.line_ids: - self._check_recursive_plan(line.command_id, visited_plans) - - def _run(self, server, plan_log_record, **kwargs): - """Run command from the Flight Plan line - - Args: - server (cx.tower.server()): Server object - plan_log_record (cx.tower.plan.log()): Log record object - kwargs (dict): Optional arguments - Following are supported but not limited to: - - "plan_log": {values passed to flightplan logger} - - "log": {values passed to command logger} - - "key": {values passed to key parser} - - """ - self.ensure_one() - - # Set current line as currently executed in log - plan_log_record.plan_line_executed_id = self - - # It is necessary to save information about which plan log - # was created for a command log that has the command action “plan” - flight_plan_command_log = kwargs.get("flight_plan_command_log") - if flight_plan_command_log: - flight_plan_command_log.triggered_plan_log_id = plan_log_record.id - - # Pass plan_log to command so it will be saved in command log - log_vals = kwargs.get("log", {}) - log_vals.update({"plan_log_id": plan_log_record.id}) - kwargs.update({"log": log_vals}) - - # Set 'sudo' value - use_sudo = self.use_sudo and server.use_sudo - - # Use sudo to bypass access rules for execute command with higher access level - command_as_root = self.sudo().command_id - - # Set path - path = self.path or command_as_root.path - server.run_command(command_as_root, path, sudo=use_sudo, **kwargs) - - def _is_executable_line(self, server, variable_values=None): - """ - Check if this line can be executed based on its condition. - - Args: - server (cx.tower.server()): The server on which conditions are checked. - variable_values (dict, optional): Custom values provided when running the - flight plan. These values are merged with server variables when - rendering the condition. - - Returns: - bool: True if the line can be executed, otherwise False. - """ - self.ensure_one() - condition = self.condition - if condition: - # Collect variable references used in the condition - variables = self.command_id.get_variables_from_code(condition) - - # Values from server variables referenced in the condition - server_values = {} - if variables: - variable_values_dict = server.get_variable_values(variables) - server_values = variable_values_dict.get(server.id, {}) or {} - - # Merge with custom values passed to the flight plan (if any) - merged_values = {**server_values, **(variable_values or {})} - - # Render condition with all available values (in pythonic mode) - if merged_values: - condition = self.command_id.render_code_custom( - condition, pythonic_mode=True, **merged_values - ) - - # For evaluate a string that contains an expression that mostly uses - # Python constants, arithmetic expressions and the objects directly provided - # in context we need use `safe_eval` - # We catch all exceptions and return False to avoid raising an exception - try: - result = safe_eval(condition) - except Exception as e: - _logger.error( - "Error evaluating condition '%s' for plan line '%s' " - "in plan '%s' for server '%s'. Line is skipped. Error: %s", - condition, - self.name, - self.plan_id.name, - server.name, - str(e), - ) - result = False - return result - - return True # Assume the line can be executed if no condition is specified - - def _skip(self, server, plan_log_record, **kwargs): - """ - Triggered when plan line skipped by condition - """ - self.ensure_one() - - # Set current line as currently executed in log - plan_log_record.plan_line_executed_id = self - - # Log the unsuccessful execution attempt - now = fields.Datetime.now() - log_vals = kwargs.get("log", {}) - - self.env["cx.tower.command.log"].record( - server.id, - self.command_id.id, - now, - now, - PLAN_LINE_CONDITION_CHECK_FAILED, - None, - _("Plan line condition check failed."), - plan_log_id=plan_log_record.id, - condition=self.condition, - is_skipped=True, - **log_vals, - ) - - def _get_dependent_model_relation_fields(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_dependent_model_relation_fields() - return res + ["action_ids"] - - def _get_pre_populated_model_data(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_pre_populated_model_data() - res.update({"cx.tower.plan.line": ["cx.tower.plan", "plan_id"]}) - return res diff --git a/addons/cetmix_tower_server/models/cx_tower_plan_line_action.py b/addons/cetmix_tower_server/models/cx_tower_plan_line_action.py deleted file mode 100644 index 04cdcc2..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_plan_line_action.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, fields, models - - -class CxTowerPlanLineAction(models.Model): - """Flight Plan Line Action""" - - _inherit = ["cx.tower.variable.mixin", "cx.tower.reference.mixin"] - _name = "cx.tower.plan.line.action" - _description = "Cetmix Tower Flight Plan Line Action" - - active = fields.Boolean(default=True) - name = fields.Char(compute="_compute_name") - sequence = fields.Integer(default=10) - line_id = fields.Many2one( - comodel_name="cx.tower.plan.line", auto_join=True, ondelete="cascade" - ) - plan_id = fields.Many2one( - comodel_name="cx.tower.plan", - related="line_id.plan_id", - store=True, - readonly=True, - ) - condition = fields.Selection( - selection=[ - ("==", "=="), - ("!=", "!="), - (">", ">"), - (">=", ">="), - ("<", "<"), - ("<=", "<="), - ], - required=True, - ) - value_char = fields.Char(string="Result", required=True) - action = fields.Selection( - selection=[ - ("e", "Exit with command exit code"), - ("ec", "Exit with custom exit code"), - ("n", "Run next command"), - ], - required=True, - default="n", - ) - custom_exit_code = fields.Integer( - help="Will be used instead of the command exit code" - ) - access_level = fields.Selection( - related="line_id.access_level", - readonly=True, - store=True, - ) - variable_value_ids = fields.One2many( - # Other field properties are defined in mixin - inverse_name="plan_line_action_id", - copy=True, - ) - - @api.depends("condition", "action", "value_char") - def _compute_name(self): - action_selection_vals = dict(self._fields["action"].selection) # type: ignore - for rec in self: - # Some values are not updated until record is not saved. - # This is a disclaimer to avoid misunderstanding - if not isinstance(rec.id, int): - rec.name = _( - "...save record to see the final expression " - "or click the line to edit" - ) - - # Compose name based on values - elif rec.condition and rec.action and rec.value_char: - action_string = action_selection_vals.get(rec.action) - - # Add custom exit code if action presumes it - if rec.action == "ec": - action_string = f"{action_string} {rec.custom_exit_code}" - rec.name = " ".join( - ( - _("If exit code"), - rec.condition, - rec.value_char, - _("then"), - action_string, - ) - ) - else: - rec.name = _("Wrong action") - - def _get_dependent_model_relation_fields(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_dependent_model_relation_fields() - return res + ["variable_value_ids"] - - def _get_pre_populated_model_data(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_pre_populated_model_data() - res.update({"cx.tower.plan.line.action": ["cx.tower.plan.line", "line_id"]}) - return res diff --git a/addons/cetmix_tower_server/models/cx_tower_plan_log.py b/addons/cetmix_tower_server/models/cx_tower_plan_log.py deleted file mode 100644 index daf9cf1..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_plan_log.py +++ /dev/null @@ -1,427 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, api, fields, models - -from .constants import PLAN_IS_EMPTY, PLAN_STOPPED - - -class CxTowerPlanLog(models.Model): - """Flight Plan Log""" - - _name = "cx.tower.plan.log" - _description = "Cetmix Tower Flight Plan Log" - _order = "start_date desc, id desc" - - active = fields.Boolean(default=True) - name = fields.Char(compute="_compute_name", compute_sudo=True, store=True) - label = fields.Char( - help="Custom label. Can be used for search/tracking", - index="trigram", - unaccent=False, - ) - server_id = fields.Many2one( - comodel_name="cx.tower.server", required=True, index=True, ondelete="cascade" - ) - plan_id = fields.Many2one( - string="Flight Plan", - comodel_name="cx.tower.plan", - required=True, - index=True, - ondelete="cascade", - ) - access_level = fields.Selection( - related="plan_id.access_level", - readonly=True, - store=True, - index=True, - ) - - # -- Time - start_date = fields.Datetime(string="Started") - finish_date = fields.Datetime(string="Finished") - duration = fields.Float( - help="Time consumed for execution, seconds", - compute="_compute_duration", - store=True, - ) - duration_current = fields.Float( - string="Duration, sec", - compute="_compute_duration_current", - help="For how long a flight plan is already running", - ) - - # -- Commands - is_running = fields.Boolean( - help="Plan is being executed right now", compute="_compute_duration", store=True - ) - is_stopped = fields.Boolean( - string="Stopped", default=False, help="Flight plan was stopped by user" - ) - plan_line_executed_id = fields.Many2one( - comodel_name="cx.tower.plan.line", - help="Flight Plan line that is being currently executed", - ) - command_log_ids = fields.One2many( - comodel_name="cx.tower.command.log", inverse_name="plan_log_id", auto_join=True - ) - plan_status = fields.Integer( - string="Status", - help="0 if plan is finished successfully. \n" - "-301 if another instance of this flight plan is running, \n" - "-302 if plan is empty, \n" - "-303 if plan reference is missing, \n" - "-304 if plan line reference is missing, \n" - "-306 if plan is not compatible with server,\n" - "-308 if plan is stopped by user", - ) - custom_message = fields.Text( - help="Custom message to be displayed in the plan log", - ) - parent_flight_plan_log_id = fields.Many2one( - "cx.tower.plan.log", string="Main Log", ondelete="cascade" - ) - scheduled_task_id = fields.Many2one( - "cx.tower.scheduled.task", - ondelete="set null", - help="Scheduled task that triggered this flight plan", - ) - variable_values = fields.Json( - default={}, - help="Custom variable values passed to the flight plan", - ) - - @api.depends("server_id.name", "name") - def _compute_name(self): - for rec in self: - rec.name = ": ".join((rec.server_id.name, rec.plan_id.name)) # type: ignore - - @api.depends("start_date", "finish_date") - def _compute_duration(self): - for plan_log in self: - # Not started yet - if not plan_log.start_date: - continue - - # If plan is finished, compute duration - if plan_log.finish_date: - plan_log.update( - { - "duration": ( - plan_log.finish_date - plan_log.start_date - ).total_seconds(), - "is_running": False, - } - ) - continue - - # If plan is running, set is_running to True - plan_log.is_running = True - - @api.depends("is_running") - def _compute_duration_current(self): - """Shows relative time between now() and start time for running plans, - and computed duration for finished ones. - """ - now = fields.Datetime.now() - for plan_log in self: - if plan_log.is_running: - plan_log.duration_current = (now - plan_log.start_date).total_seconds() - else: - plan_log.duration_current = plan_log.duration - - def start(self, server, plan, start_date=None, **kwargs): - """ - Runs plan on server. - Creates initial log records for each command that cannot be executed until - it finds the first executable command. - - Args: - server (cx.tower.server()) server. - plan (cx.tower.plan()) Flight Plan. - start_date (datetime) flight plan start date time. - **kwargs (dict): optional values - Following keys are supported but not limited to: - - "plan_log": {values passed to flightplan logger} - - "log": {values passed to logger} - - "key": {values passed to key parser} - - "no_command_log" (bool): If True, no logs will be recorded for - non-executable lines. - - "variable_values", dict(): custom variable values - in the format of `{variable_reference: variable_value}` - eg `{'odoo_version': '16.0'}` - Will be applied only if user has write access to the server. - Returns: - cx.tower.plan.log(): New flightplan log record. - """ - - def get_executable_line(plan, server, variable_values=None): - """ - Generator to get each line and check if it's executable. - """ - for line in plan.line_ids: - yield ( - line, - line._is_executable_line(server, variable_values=variable_values), - ) - - vals = { - "server_id": server.id, - "plan_id": plan.id, - "is_running": True, - "start_date": start_date or fields.Datetime.now(), - } - - # Extract and apply plan log kwargs - plan_log_kwargs = kwargs.get("plan_log") - if plan_log_kwargs: - vals.update(plan_log_kwargs) - - # Extract and apply variable values - variable_values = kwargs.get("variable_values") - if variable_values: - vals["variable_values"] = variable_values - - plan_log = self.sudo().create(vals) - - # Process each line until the first executable one is found - for line, is_executable in get_executable_line( - plan, server, variable_values=variable_values - ): - if is_executable: - line._run(server, plan_log, **kwargs) - break - else: - if self._context.get("no_command_log"): - continue - line._skip( - server, - plan_log, - log={"variable_values": dict(variable_values or {})}, - ) - break - else: - plan_log.finish(plan_status=PLAN_IS_EMPTY) - - return plan_log - - def stop(self): - """ - Force stop this plan log (and currently running command if possible). - """ - user_name = self.env.user.name - for log in self: - if not log.is_running: - continue - - # Finish plan log - log.finish( - plan_status=PLAN_STOPPED, - custom_message=_("Stopped by user %(user)s", user=user_name), - is_stopped=True, - ) - - # Stop running command - running_cmd_logs = log.command_log_ids.filtered(lambda c: c.is_running) - running_cmd_logs.stop() - - def action_stop(self): - """ - Action to stop the running plans. - """ - self.stop() - - if len(self) > 1: # more than one plan is running - title = _("Flight Plans Stopped") - message = ", ".join([plan.name for plan in self]) - else: - title = _("Flight Plan Stopped") - message = self.name - - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": title, - "message": message, - "sticky": False, - "next": { - "type": "ir.actions.act_window_close", - }, - }, - } - - def finish(self, plan_status, **kwargs): - """Finish plan execution - - Args: - plan_status (Integer) plan execution code - **kwargs (dict): optional values - """ - values = { - "is_running": False, - "plan_status": plan_status, - "finish_date": fields.Datetime.now(), - } - - self.ensure_one() - - # Apply kwargs - if kwargs: - values.update(kwargs) - self.sudo().write(values) - - # Call hook - self._plan_finished() - - # Check if we were deleting a server - if ( - self.server_id._is_being_deleted() - and self.server_id.plan_delete_id == self.plan_id - ): - if plan_status == 0: - # And finally delete the server - self.with_context(server_force_delete=True).server_id.unlink() - else: - # Set deletion error if flightplan failed - self.server_id.status = "delete_error" - - def record(self, server, plan, status, start_date=None, finish_date=None, **kwargs): - """ - Record plan log without running it. - - Args: - server (cx.tower.server()) server. - plan (cx.tower.plan()) Flight Plan. - status (int) plan execution code - start_date (datetime) flight plan start date time. - finish_date (datetime) flight plan finish date time. - **kwargs (dict): optional values - Following keys are supported but not limited to: - - "plan_log": {values passed to flightplan logger} - - "log": {values passed to logger} - - "key": {values passed to key parser} - - "no_command_log" (bool): If True, no logs will be recorded for - non-executable lines. - Returns: - cx.tower.plan.log(): New flightplan log record. - """ - - default_date = fields.Datetime.now() - vals = { - "server_id": server.id, - "plan_id": plan.id, - "start_date": start_date or default_date, - "finish_date": finish_date or default_date, - "plan_status": status, - } - - # Extract and apply plan log kwargs - plan_log_kwargs = kwargs.get("plan_log") - if plan_log_kwargs: - vals.update(plan_log_kwargs) - - plan_log = self.sudo().create(vals) - plan_log._plan_finished() - return plan_log - - def _plan_finished(self): - """Triggered when flightplan in finished - Inherit to implement your own hooks - - Returns: - bool: True if event was handled - """ - - self.ensure_one() - - # Do not notify if a plan that was run from another plan has been executed - if self.parent_flight_plan_log_id: - return True - - # Check if notifications are enabled - ICP_sudo = self.env["ir.config_parameter"].sudo() - notification_type_success = ICP_sudo.get_param( - "cetmix_tower_server.notification_type_success" - ) - notification_type_error = ICP_sudo.get_param( - "cetmix_tower_server.notification_type_error" - ) - - # Prepare notifications - if not notification_type_success and not notification_type_error: - return True - - # Use context timestamp to avoid timezone issues - context_timestamp = fields.Datetime.context_timestamp( - self, fields.Datetime.now() - ) - - # Action for button - action = self.env["ir.actions.act_window"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_plan_log" - ) - - context = self.env.context.copy() - params = dict(context.get("params") or {}) - params["button_name"] = _("View Log") - context["params"] = params - - # Add record id and context to the action - action.update( - { - "context": context, - "res_id": self.id, - "views": [(False, "form")], - } - ) - - # Send notification - if self.plan_status == 0 and notification_type_success: - # Success notification - self.create_uid.notify_success( - message=_( - "%(timestamp)s
    " "Flight Plan '%(name)s' finished successfully", - name=self.plan_id.name, - timestamp=context_timestamp, - ), - title=self.server_id.name, - sticky=notification_type_success == "sticky", - action=action, - ) - - # Error notification - if self.plan_status != 0 and notification_type_error: - self.create_uid.notify_danger( - message=_( - "%(timestamp)s
    " - "Flight Plan '%(name)s'" - " finished with error", - name=self.plan_id.name, - timestamp=context_timestamp, - ), - title=self.server_id.name, - sticky=notification_type_error == "sticky", - action=action, - ) - return True - - def _plan_command_finished(self, command_log): - """This function is triggered when a command from this log is finished. - Next action is triggered based on command status (ak exit code) - - Args: - command_log (cx.tower.command.log()): Command log object - - """ - self.ensure_one() - - # Prevent scheduling further actions if this log was stopped - if self.is_stopped: - return - - # Update plan log variable values from command log - # Overwrite with command log values (last command's values take precedence) - self.variable_values = command_log.variable_values - - # Get next line to execute - self.plan_id._run_next_action(command_log) # type: ignore diff --git a/addons/cetmix_tower_server/models/cx_tower_reference_mixin.py b/addons/cetmix_tower_server/models/cx_tower_reference_mixin.py deleted file mode 100644 index ea396ca..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_reference_mixin.py +++ /dev/null @@ -1,481 +0,0 @@ -# Copyright (C) 2024 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import re -from collections import defaultdict - -from odoo import _, api, fields, models -from odoo.osv import expression -from odoo.tools import ormcache - - -class CxTowerReferenceMixin(models.AbstractModel): - """ - Used to create and manage unique record references. - """ - - _name = "cx.tower.reference.mixin" - _description = "Cetmix Tower reference mixin" - _rec_names_search = ["name", "reference"] - - # Used to check the reference before it's being fixed. - # Ensures there's at least one valid symbol - # that can be used later as a new reference basis. - REFERENCE_PRELIMINARY_PATTERN = r"[\da-zA-Z]" - - name = fields.Char(required=True, index="trigram") - reference = fields.Char( - index=True, - unaccent=False, - help="Can contain English letters, digits and '_'. Leave blank to autogenerate", - ) - - _sql_constraints = [ - ("reference_unique", "UNIQUE(reference)", "Reference must be unique") - ] - - @api.model_create_multi - def create(self, vals_list): - """ - Overrides create to ensure 'reference' is auto-corrected - or validated for each record. - - Add `reference_mixin_override` context key to skip the reference check - - Args: - vals_list (list[dict]): List of dictionaries with record values. - - Returns: - Records: The created record(s). - """ - - if vals_list and not self._context.get("reference_mixin_override"): - # Check if we need to populate references based on parent record - auto_generate_settings = self._get_pre_populated_model_data().get( - self._name - ) - if auto_generate_settings: - parent_model, relation_field = auto_generate_settings - vals_list = self._pre_populate_references( - parent_model, relation_field, vals_list - ) - - # Fix or create references - for vals in vals_list: - if not vals: - continue - - # Remove leading and trailing whitespaces from name - vals_name = vals.get("name") - name = vals_name.strip() if vals_name else vals_name - - # Remove leading and trailing whitespaces from reference - vals_reference = vals.get("reference") - reference = vals_reference.strip() if vals_reference else vals_reference - - # Nothing can be done if no name or reference is provided - if not name and not reference: - continue - - # Save name back to vals if it was modified - if vals_name != name: - vals["name"] = name - - # Generate reference - vals.update( - {"reference": self._generate_or_fix_reference(reference or name)} - ) - - res = super().create(vals_list) - self.clear_caches() - return res - - def write(self, vals): - """ - Updates record, auto-correcting or validating 'reference' - based on 'name' or existing value. - - Add `reference_mixin_override` context key to skip the reference check - - Args: - vals (dict): Values to update, may include 'reference'. - - Returns: - Result of the super `write` call. - """ - if not self._context.get("reference_mixin_override") and "reference" in vals: - reference = vals.get("reference", False) - if not reference: - # Get name from vals - updated_name = vals.get("name") - - # No name in vals. Update records one by one - if not updated_name: - for record in self: - record_vals = vals.copy() - record_vals.update( - {"reference": self._generate_or_fix_reference(record.name)} - ) - super(CxTowerReferenceMixin, record).write(record_vals) - return True - # Name is present in vals - reference = self._generate_or_fix_reference(updated_name) - else: - reference = self._generate_or_fix_reference(reference) - vals.update({"reference": reference}) - - res = super().write(vals) - - # Update references of dependent models - if "reference" in vals: - self._update_dependent_model_references() - # Clear caches - self.clear_caches() - return res - - def unlink(self): - """ - Overrides unlink to clear cache for this method - """ - res = super().unlink() - self.clear_caches() - return res - - def copy(self, default=None): - """ - Overrides the copy method to ensure unique reference values - for duplicated records. - - Args: - default (dict, optional): Default values for the new record. - - Returns: - Record: The newly copied record with adjusted defaults. - """ - self.ensure_one() - if default is None: - default = {} - - # skip copying 'name' because this function can be used in models - # where 'name' field is not stored - if not self.env.context.get("reference_mixin_skip_copy"): - default["name"] = self._get_copied_name(force_name=default.get("name")) - if "reference" not in default: - default["reference"] = self._generate_or_fix_reference(default["name"]) - return super().copy(default=default) - - def _get_reference_pattern(self): - """ - Returns the regex pattern used for validating and correcting references. - This allows for easy modification of the pattern in one place. - - Important: pattern must be enclosed in square brackets! - - Returns: - str: A regex pattern - """ - return "[a-z0-9_]" - - def _get_pre_populated_model_data(self): - """Returns List of models that should try to generate - references based on the related model reference. - - Eg flight plan lines references are generated based on the flight plan one. - - Returns: - dict: Model values dictionary: - {model_name: [parent_model, relation_field]} - """ - return {} - - def _get_extra_vals_fields(self): - """Returns list of extra fields that are needed for reference generation. - This method if used to make custom reference generation logic more flexible. - Eg for 'cx.tower.variable.value': - 'server_id', 'server_template_id', 'plan_line_action_id'. - So for common models like 'cx.tower.server' this method is not needed. - - Returns: - list: List of fields: - [field_name1, field_name2, ...] - """ - return [] - - def _get_dependent_model_relation_fields(self): - """Returns list of fields that reference dependent models. - - Eg flight plan lines references are generated based on the flight plan one. - - Returns: - list: List of fields: - [field_name1, field_name2, ...] - """ - return [] - - def _update_dependent_model_references(self): - """Update references of dependent models""" - dependent_model_relation_fields = self._get_dependent_model_relation_fields() - if dependent_model_relation_fields: - for field in dependent_model_relation_fields: - related_model_name = self[field]._name - - # Check if the related model has auto-generate settings - auto_generate_settings = ( - self[field]._get_pre_populated_model_data().get(related_model_name) - ) - if auto_generate_settings: - parent_model, relation_field = auto_generate_settings - else: - continue - - # Parse the field for all records - for record in self: - related_records = record[field] - # Get vals list - rec_vals_list = related_records.read( - [relation_field] + related_records._get_extra_vals_fields() - ) - # Transform Many2one tuples to IDs - for rv in rec_vals_list: - for k, v in rv.items(): - # Transform m2o fields from (id, name) to id - if isinstance(v, tuple): - rv[k] = v[0] - related_records._pre_populate_references( - parent_model, relation_field, rec_vals_list - ) - ref_by_id = {rv["id"]: rv["reference"] for rv in rec_vals_list} - for related_record in related_records: - related_record.reference = ref_by_id[related_record.id] - - def _generate_or_fix_reference(self, reference_source): - """ - Generate a new reference of fix an existing one. - - Args: - reference_source (str): Original string. - - Returns: - str: Generated or fixed reference. - """ - - # Check if reference matches the pattern - reference_pattern = self._get_reference_pattern() - - if re.fullmatch(rf"{reference_pattern}+", reference_source): - reference = reference_source - - # Fix reference if it doesn't match - else: - # Modify the pattern to be used in `sub` - inner_pattern = reference_pattern[1:-1] - reference = ( - re.sub( - rf"[^{inner_pattern}]", - "", - reference_source.strip().replace(" ", "_").lower(), - ) - or self._get_model_generic_reference() - ) - - # Check if the same reference already exists and add a suffix if yes - counter = 1 - final_reference = reference - - # If exclude same records from search results - if self and not self.env.context.get("reference_mixin_skip_self"): - domain = [("id", "not in", self.ids)] - else: - domain = [] - final_domain = expression.AND([domain, [("reference", "=", final_reference)]]) - - # Search all records without restrictions including archived - self_with_sudo_and_context = self.sudo().with_context(active_test=False) - while self_with_sudo_and_context.search_count(final_domain) > 0: - counter += 1 - final_reference = f"{reference}_{counter}" - final_domain = expression.AND( - [domain, [("reference", "=", final_reference)]] - ) - - return final_reference - - def _get_copied_name(self, force_name=None): - """ - Return a copied name of the record - by adding the suffix (copy) at the end - and counter until the name is unique. - - Args: - force_name (str): Used to use force name instead of record name. - - Returns: - An unique name for the copied record - """ - self.ensure_one() - original_name = force_name or self.name - copy_name = _("%(name)s (copy)", name=original_name) - - counter = 1 - # Ensures that the generated copy name is unique by - # appending a counter until a unique name is found. - while self.search_count([("name", "=", copy_name)]) > 0: - counter += 1 - copy_name = _( - "%(name)s (copy %(number)s)", - name=original_name, - number=str(counter), - ) - - return copy_name - - def _get_model_generic_reference(self): - """Get generic reference for current model. - Generic references are used as a fallback in the automatic - reference generation. - When a reference cannot be generated neither from the 'reference' - nor from the 'name' field values. - - Eg for the 'cx.tower.plan' model such reference will look like - 'tower_plan'. - - Returns: - Char: generated prefix - """ - model_prefix = self._name.replace("cx.tower.", "").replace(".", "_") - return model_prefix - - def get_by_reference(self, reference): - """Get record based on its reference. - - Important: references are case sensitive! - - Args: - reference (Char): record reference - - Returns: - Record: Record that matches provided reference - """ - return self.browse(self._get_id_by_reference(reference)) - - @ormcache("self.env.uid", "self.env.su", "reference") - def _get_id_by_reference(self, reference): - """Get record id based on its reference. - - Important: references are case sensitive! - - Args: - reference (Char): record reference - - Returns: - Record: Record id that matches provided reference - """ - records = self.search([("reference", "=", reference)]) - - # This is in case some models will remove reference uniqueness constraint - return records and records[0].id - - @api.model - def _prepare_references(self, model, key_name, vals_list): - """ - Prepare a dictionary of references for given model records. - - This function extracts unique IDs from a list of dictionaries (vals_list) - based on a specified key (key_name), fetches the corresponding records - from the specified model, and returns a dictionary mapping record IDs to - their references. - - Args: - model (str): The name of the model to fetch records from. - key_name (str): The key in the dictionaries of vals_list that contains - the record IDs. - vals_list (list of dict): A list of dictionaries containing the values - to be processed. - - Returns: - dict: A dictionary mapping record IDs to their references. - """ - if not vals_list: - # No entries to process, return an empty dictionary - return {} - - try: - CxModel = self.env[model] - except KeyError as err: - raise ValueError( - _( - ( - "Model '%(model)s' does not exist. " - "Please provide a valid model name." - ), - model=model, - ) - ) from err - - # Extract all unique ids from vals_list - line_ids = { - vals.get(key_name) - for vals in vals_list - if vals.get(key_name) and not vals.get("reference") - } - - # Fetch all line references in a single query - lines = CxModel.browse(line_ids) - return {line.id: line.reference for line in lines if line.reference} - - @api.model - def _pre_populate_references(self, model_name, field_name, vals_list): - """ - Populates reference fields in a list of dictionaries (vals_list) - intended for record creation. - - This method generates unique references for each dictionary entry in - `vals_list` based on a specified field that links to records in - another model (indicated by `model_name`). It uses existing references - from the related records as a basis and appends a suffix and an - incrementing index to ensure uniqueness. - If reference is present in values it will not be overwritten. - - Args: - model_name (str): The name of the related model to extract - reference data from. - field_name (str): The key in each dictionary in `vals_list` - containing the related record's ID. - vals_list (list of dict): A list of dictionaries where each dictionary - represents values for a new record. - - Returns: - list: The modified `vals_list`, with a unique 'reference' - entry in each dictionary. - """ - - # Extract parent record references from vals_list - parent_record_refs = self._prepare_references(model_name, field_name, vals_list) - line_index_dict = defaultdict(int) - - # Used to make reference more readable - model_reference = self._get_model_generic_reference() - - # Populate vals with references - for vals in vals_list: - # Skip if reference is provided explicitly and has symbols - existing_reference = vals.get("reference") - if existing_reference and bool( - re.search(self.REFERENCE_PRELIMINARY_PATTERN, existing_reference) - ): - continue - - # Compose based on related record reference if exists - record_id = vals.get(field_name) - if record_id and parent_record_refs.get(record_id): - line_index_dict[record_id] += 1 - line_index = line_index_dict[record_id] - vals[ - "reference" - ] = f"{parent_record_refs[record_id]}_{model_reference}_{line_index}" - else: - # Handle cases where the field is not present - line_index_dict["no_record"] += 1 - line_index = line_index_dict["no_record"] - vals["reference"] = f"no_{model_reference}_{line_index}" - - return vals_list diff --git a/addons/cetmix_tower_server/models/cx_tower_scheduled_task.py b/addons/cetmix_tower_server/models/cx_tower_scheduled_task.py deleted file mode 100644 index a5dea54..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_scheduled_task.py +++ /dev/null @@ -1,296 +0,0 @@ -import logging -from datetime import timedelta - -from dateutil.relativedelta import relativedelta - -from odoo import _, api, fields, models - -_logger = logging.getLogger(__name__) - - -class CxTowerScheduledTask(models.Model): - _name = "cx.tower.scheduled.task" - _description = "Scheduled Task" - _inherit = ["cx.tower.access.role.mixin", "cx.tower.reference.mixin"] - _order = "sequence, next_call" - - active = fields.Boolean(default=True) - sequence = fields.Integer(default=10) - server_ids = fields.Many2many( - "cx.tower.server", - "cx_tower_scheduled_task_server_rel", - "scheduled_task_id", - "server_id", - string="Servers", - ) - server_template_ids = fields.Many2many( - string="Server Templates", - comodel_name="cx.tower.server.template", - relation="cx_tower_server_template_scheduled_task_rel", - column1="scheduled_task_id", - column2="server_template_id", - ) - action = fields.Selection( - [("command", "Command"), ("plan", "Flight Plan")], required=True - ) - command_id = fields.Many2one("cx.tower.command", string="Command") - plan_id = fields.Many2one(string="Flight Plan", comodel_name="cx.tower.plan") - is_running = fields.Boolean(default=False, readonly=True) - interval_number = fields.Integer(default=1, help="Repeat every x.") - interval_type = fields.Selection( - [ - ("minutes", "Minutes"), - ("hours", "Hours"), - ("days", "Days"), - ("weeks", "Weeks"), - ("months", "Months"), - ], - string="Interval Unit", - default="months", - ) - next_call = fields.Datetime( - string="Next Execution Date", - required=True, - default=fields.Datetime.now, - help="Next planned execution date for this task.", - ) - last_call = fields.Datetime( - string="Last Execution Date", help="Previous time the task ran successfully." - ) - custom_variable_value_ids = fields.One2many( - "cx.tower.scheduled.task.cv", - "scheduled_task_id", - string="Custom Variable Values", - ) - warning_message = fields.Text( - compute="_compute_warning_message", - ) - - # ---- Access. Add relation for mixin fields - user_ids = fields.Many2many( - relation="cx_tower_scheduled_task_user_rel", - ) - manager_ids = fields.Many2many( - relation="cx_tower_scheduled_task_manager_rel", - ) - - _sql_constraints = [ - ( - "interval_positive", - "CHECK (interval_number > 0)", - "Interval number must be greater than zero.", - ), - ] - - @api.depends("interval_number", "interval_type") - def _compute_warning_message(self): - """ - Show warning on the task form if interval in the scheduled task - is less than interval in the underlaying cron job. - """ - cron = self.env.ref( - "cetmix_tower_server.ir_cron_run_scheduled_tasks", raise_if_not_found=False - ) - if not cron: - self.warning_message = False - return - - # Using now's date as the base point ensures a consistent and comparable - # reference when calculating the next scheduled execution for both the cron - # and the tasks. - now = fields.Datetime.now() - # _get_next_call is designed for tasks, but can also be used for the - # cron record, as both share the same interval fields. This keeps interval - # comparison logic consistent. - cron_next = self._get_next_call(cron, now) - - for task in self: - task_next = self._get_next_call(task, now) - if task_next < cron_next: - task.warning_message = _( - "The selected task interval is too low in relation to the general " - "system settings. This may lead to task execution delays." - ) - else: - task.warning_message = False - - def action_run(self): - """ - Run scheduled action and reschedule next call. - """ - return self._run() - - def action_open_command_logs(self): - """ - Open current scheduled task command log records - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_command_log" - ) - action["domain"] = [("scheduled_task_id", "=", self.id)] # pylint: disable=no-member - return action - - def action_open_plan_logs(self): - """ - Open current scheduled task flightplan log records - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_plan_log" - ) - action["domain"] = [("scheduled_task_id", "=", self.id)] # pylint: disable=no-member - return action - - @api.model - def _run_scheduled_tasks(self): - """ - Cron: finds due tasks and runs their actions (command/plan). - Handles errors per-task and reserves tasks atomically to avoid double execution. - """ - now = fields.Datetime.now() - due_tasks = self.search( - [ - ("next_call", "<=", now), - ("active", "=", True), - ("is_running", "=", False), - ] - ) - if not due_tasks: - return - - due_tasks.with_context(from_cron=True)._run() - - def _run(self): - """ - Run scheduled action and reschedule next call. - """ - tasks = self._reserve_tasks() - if not tasks: - return - - if self.env.context.get("from_cron"): - # WARNING: Explicit commit! - # This commit is made **only** when called from cron (context["from_cron"]). - # Reason: To atomically reserve scheduled tasks by setting is_running=True, - # so that only one cron worker processes each task, even if multiple workers - # pick up the cron job at the same time. Without this commit, the change - # would not be visible to other transactions until the end of the cron - # transaction, leading to a race condition and possible double execution. - # Explicit commits are strongly discouraged in Odoo business logic and - # should be used only with clear justification and in strictly controlled - # contexts (like this cron scenario). Never add this commit for general - # business flows! - self.env.cr.commit() # pylint: disable=invalid-commit - - errors = [] - for task in tasks: - try: - with self.env.cr.savepoint(): - if task.action == "command" and task.command_id: - task._run_command() - elif task.action == "plan" and task.plan_id: - task._run_plan() - except Exception as e: - _logger.exception(f"Scheduled task {task.id} failed: {e}") - - task_error = _( - "Unable to run scheduled task '%(f)s'. Error: %(e)s", - f=task.display_name, - e=e, - ) - errors.append(task_error) - - finally: - finished_at = fields.Datetime.now() - # Always update the scheduling, even if the task failed - task.write( - { - "last_call": finished_at, - "next_call": self._get_next_call(task, finished_at), - "is_running": False, - } - ) - - if errors: - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("Failure"), - "message": "\n".join(errors), - }, - } - - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("Success"), - "message": _("Scheduled tasks run successfully."), - }, - } - - def _get_next_call(self, task, from_date): - """ - Calculate next_call datetime - """ - num = task.interval_number or 1 - intervals = { - "minutes": timedelta(minutes=num), - "hours": timedelta(hours=num), - "days": timedelta(days=num), - "weeks": timedelta(weeks=num), - "months": relativedelta(months=num), - } - return from_date + intervals.get(task.interval_type, timedelta()) - - def _run_command(self): - """Run command on selected servers.""" - variable_values = { - value.variable_id.reference: value.value_char - for value in self.custom_variable_value_ids - } - kwargs = { - "log": {"scheduled_task_id": self.id}, - "variable_values": variable_values, - } - for server in self.server_ids: - server.run_command(self.command_id, **kwargs) - - def _run_plan(self): - """Run flight plan on selected servers.""" - variable_values = { - value.variable_id.reference: value.value_char - for value in self.custom_variable_value_ids - } - kwargs = { - "plan_log": {"scheduled_task_id": self.id}, - "variable_values": variable_values, - } - - for server in self.server_ids: - server.run_flight_plan(self.plan_id, **kwargs) - - def _reserve_tasks(self, limit=None): - """ - Atomically select and lock free tasks for processing. - """ - sql = """ - SELECT id - FROM cx_tower_scheduled_task - WHERE is_running = FALSE AND id IN %s - ORDER BY id - """ - params = [tuple(self.ids)] - if limit: - sql += " LIMIT %s" - params.append(limit) - sql += " FOR UPDATE SKIP LOCKED" - self.env.cr.execute(sql, tuple(params)) - - task_ids = [row[0] for row in self.env.cr.fetchall()] - if not task_ids: - return self.browse() - - tasks = self.browse(task_ids) - tasks.write({"is_running": True}) - return tasks diff --git a/addons/cetmix_tower_server/models/cx_tower_scheduled_task_cv.py b/addons/cetmix_tower_server/models/cx_tower_scheduled_task_cv.py deleted file mode 100644 index 47a0925..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_scheduled_task_cv.py +++ /dev/null @@ -1,18 +0,0 @@ -from odoo import fields, models - - -class CxTowerScheduledTaskCv(models.Model): - """ - Custom variable values for scheduled tasks. - """ - - _inherit = "cx.tower.custom.variable.value.mixin" - _name = "cx.tower.scheduled.task.cv" - _description = "Custom variable values for scheduled tasks" - - scheduled_task_id = fields.Many2one( - "cx.tower.scheduled.task", - string="Scheduled Task", - required=True, - ondelete="cascade", - ) diff --git a/addons/cetmix_tower_server/models/cx_tower_server.py b/addons/cetmix_tower_server/models/cx_tower_server.py deleted file mode 100644 index 6979a59..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_server.py +++ /dev/null @@ -1,1986 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import ast -import io -import logging -from datetime import timedelta -from functools import wraps - -from odoo import _, api, fields, models -from odoo.exceptions import UserError, ValidationError -from odoo.tools.safe_eval import safe_eval - -from odoo.addons.base.models.res_users import check_identity - -from ..ssh.ssh import SSHConnection, SSHManager -from .constants import ( - ANOTHER_COMMAND_RUNNING, - COMMAND_NOT_COMPATIBLE_WITH_SERVER, - COMMAND_TIMED_OUT, - COMMAND_TIMED_OUT_MESSAGE, - FILE_CREATION_FAILED, - GENERAL_ERROR, - NO_COMMAND_RUNNER_FOUND, - PYTHON_COMMAND_ERROR, - SSH_CONNECTION_ERROR, -) -from .tools import generate_random_id - -_logger = logging.getLogger(__name__) - - -def ensure_ssh_disconnect(func): - """ - Decorator that ensures the SSH connection is disconnected after the transaction - completes, whether by commit or rollback. - - This decorator registers hooks (postcommit and postrollback) before calling the - decorated function. Thus, even if the function raises an exception (and it's caught - at a higher level), the hooks will still be executed, ensuring that the - SSH connection is closed. - """ - - @wraps(func) - def wrapped(self, *args, **kwargs): - # Try to obtain the SSH connection once - try: - connection = self._get_ssh_client(raise_on_error=True) - except Exception as e: - _logger.error(f"Error obtaining SSH connection: {e}") - connection = None - - # Define a hook to disconnect the SSH connection using the obtained connection. - def disconnect_connection(): - if connection: - try: - connection.disconnect() - except Exception as e: - _logger.error(f"Error disconnecting SSH connection: {e}") - - # Register the disconnect hook for both commit and rollback events. - self.env.cr.postcommit.add(disconnect_connection) - self.env.cr.postrollback.add(disconnect_connection) - - # Call the decorated function. - result = func(self, *args, **kwargs) - return result - - return wrapped - - -class CxTowerServer(models.Model): - """Represents a server entity - - Keeps information required to connect and perform routine operations - such as configuration, file management etc" - - """ - - _name = "cx.tower.server" - _inherit = [ - "cx.tower.access.role.mixin", - "cx.tower.variable.mixin", - "cx.tower.reference.mixin", - "mail.thread", - "mail.activity.mixin", - "cx.tower.vault.mixin", - "cx.tower.tag.mixin", - ] - _description = "Cetmix Tower Server" - _order = "name asc" - - SECRET_FIELDS = ["ssh_password", "host_key"] - - # ---- Main - active = fields.Boolean(default=True) - color = fields.Integer(help="For better visualization in views") - partner_id = fields.Many2one(comodel_name="res.partner") - status = fields.Selection( - selection=lambda self: self._selection_status(), - default=None, - required=False, - ) - - # ---- Connection - ip_v4_address = fields.Char( - string="IPv4 Address", groups="cetmix_tower_server.group_manager" - ) - ip_v6_address = fields.Char( - string="IPv6 Address", groups="cetmix_tower_server.group_manager" - ) - skip_host_key = fields.Boolean( - default=False, - help="Enable to skip host key verification", - ) - host_key = fields.Char( - groups="cetmix_tower_server.group_manager", - help="Host key to verify the server", - ) - ssh_port = fields.Integer( - string="SSH port", - required=True, - default=22, - groups="cetmix_tower_server.group_manager", - ) - ssh_username = fields.Char( - string="SSH Username", required=True, groups="cetmix_tower_server.group_manager" - ) - ssh_password = fields.Char( - string="SSH Password", - groups="cetmix_tower_server.group_manager", - ) - ssh_key_id = fields.Many2one( - comodel_name="cx.tower.key", - string="SSH Private Key", - domain=[("key_type", "=", "k")], - groups="cetmix_tower_server.group_manager", - ) - ssh_auth_mode = fields.Selection( - string="SSH Auth Mode", - selection=[ - ("p", "Password"), - ("k", "Key"), - ], - default="p", - required=True, - groups="cetmix_tower_server.group_manager", - ) - use_sudo = fields.Selection( - string="Use sudo", - selection=[("n", "Without password"), ("p", "With password")], - help="Run commands using 'sudo'. Leave empty if 'sudo' is not needed.", - groups="cetmix_tower_server.group_manager", - ) - url = fields.Char( - string="URL", help="Server web interface, eg 'https://doge.example.com'" - ) - # ---- Variables - variable_value_ids = fields.One2many( - inverse_name="server_id" # Other field properties are defined in mixin - ) - - # ---- Keys - secret_ids = fields.One2many( - string="Secrets", - comodel_name="cx.tower.key.value", - inverse_name="server_id", - groups="cetmix_tower_server.group_manager", - ) - - # ---- Attributes - os_id = fields.Many2one( - string="Operating System", - comodel_name="cx.tower.os", - groups="cetmix_tower_server.group_manager", - ) - tag_ids = fields.Many2many( - relation="cx_tower_server_tag_rel", - column1="server_id", - column2="tag_id", - ) - note = fields.Text() - command_log_ids = fields.One2many( - comodel_name="cx.tower.command.log", inverse_name="server_id" - ) - plan_log_ids = fields.One2many( - comodel_name="cx.tower.plan.log", inverse_name="server_id" - ) - file_ids = fields.One2many( - "cx.tower.file", - "server_id", - string="Files", - ) - file_count = fields.Integer( - "Total Files", - compute="_compute_file_count", - ) - - # ---- Server logs - server_log_ids = fields.One2many( - string="Server Logs", - comodel_name="cx.tower.server.log", - inverse_name="server_id", - ) - - # ---- Related server template - server_template_id = fields.Many2one( - "cx.tower.server.template", - readonly=True, - index=True, - ) - - # ---- Delete plan - plan_delete_id = fields.Many2one( - "cx.tower.plan", - string="On Delete Plan", - groups="cetmix_tower_server.group_manager", - help="This Flightplan will be run when the server is deleted", - ) - - # ---- Access. Add relation for mixin fields - - user_ids = fields.Many2many( - relation="cx_tower_server_user_rel", - ) - manager_ids = fields.Many2many( - relation="cx_tower_server_manager_rel", - ) - - # ---- Shortcuts - shortcut_ids = fields.Many2many( - comodel_name="cx.tower.shortcut", - relation="cx_tower_server_shortcut_rel", - column1="server_id", - column2="shortcut_id", - string="Shortcuts", - ) - - # ---- Scheduled Tasks - scheduled_task_ids = fields.Many2many( - comodel_name="cx.tower.scheduled.task", - relation="cx_tower_scheduled_task_server_rel", - column1="server_id", - column2="scheduled_task_id", - string="Scheduled Tasks", - ) - - command_ids = fields.Many2many( - comodel_name="cx.tower.command", - relation="cx_tower_server_command_rel", - column1="server_id", - column2="command_id", - string="Commands", - ) - - plan_ids = fields.Many2many( - comodel_name="cx.tower.plan", - relation="cx_tower_plan_cx_tower_server_rel", - column1="cx_tower_server_id", - column2="cx_tower_plan_id", - string="Flight Plans", - ) - - def _selection_status(self): - """ - Status selection options - - Returns: - list: status selection options - """ - return [ - ("stopped", "Stopped"), - ("starting", "Starting"), - ("running", "Running"), - ("stopping", "Stopping"), - ("restarting", "Restarting"), - ("deleting", "Deleting"), - ("delete_error", "Deletion Error"), - ] - - def _compute_file_count(self): - """Compute total server files""" - for server in self: - server.file_count = len(server.file_ids) - - @api.constrains("ip_v4_address", "ip_v6_address", "ssh_auth_mode") - def _constraint_ssh_settings(self): - """Ensure SSH settings are valid. - Set 'skip_ssh_settings_check' context key to skip the checks - """ - # Skip the check if context key is set - if self._context.get("skip_ssh_settings_check"): - return - - for rec in self: - # Combine all errors together - validation_errors = [] - if not rec.ip_v4_address and not rec.ip_v6_address: - validation_errors.append( - _( - "Please provide IPv4 or IPv6 address for %(srv)s", - srv=rec.name, - ) - ) - if rec.ssh_auth_mode == "k" and not rec.ssh_key_id: - validation_errors.append( - _("Please provide SSH Key for %(srv)s", srv=rec.name) - ) - - # Raise errors if any - if validation_errors: - validation_error = "\n".join(validation_errors) - raise ValidationError(validation_error) - - @api.model_create_multi - def create(self, vals_list): - """Override create to validate SSH password before record creation.""" - # Validate SSH password before creating records - if not self._context.get("skip_ssh_settings_check"): - validation_errors = [] - for vals in vals_list: - if vals.get("ssh_auth_mode") == "p" and not vals.get("ssh_password"): - server_name = vals["name"] - validation_errors.append( - _("Please provide SSH password for %(srv)s", srv=server_name) - ) - - if validation_errors: - raise ValidationError("\n".join(validation_errors)) - - return super().create(vals_list) - - def unlink(self): - """Run post-delete flight plan""" - servers_to_delete = self.env["cx.tower.server"] - flight_plan_log_obj = self.env["cx.tower.plan.log"] - - for server in self: - # If forced, no delete plan, or already in deleting state, - # skip plan running - if ( - self._context.get("server_force_delete") - or not server.plan_delete_id - or server._is_being_deleted() - ): - servers_to_delete |= server - continue - - plan_label = generate_random_id(4) - server.plan_delete_id._run_single(server, plan_log={"label": plan_label}) - plan_log = flight_plan_log_obj.search( - [ - ("server_id", "=", server.id), - ("plan_id", "=", server.plan_delete_id.id), - ("label", "=", plan_label), - ] - ) - - # If plan has finished, either mark for deletion or set an error - if plan_log and plan_log.finish_date: - if plan_log.plan_status == 0: - servers_to_delete |= server - else: - server.status = "delete_error" - else: - # Plan still in progress - server.status = "deleting" - - return super(CxTowerServer, servers_to_delete).unlink() - - @api.returns("self", lambda value: value.id) - def copy(self, default=None): - default = default or {} - default["status"] = None - - file_ids = self.env["cx.tower.file"] - for file in self.file_ids: - file_ids |= file.copy( - { - "auto_sync": False, - "keep_when_deleted": True, - } - ) - default["file_ids"] = file_ids.ids - - # Copy SSH password and host key - default["ssh_password"] = self._get_secret_value("ssh_password") - default["host_key"] = self._get_secret_value("host_key") - result = super().copy(default=default) - - # Copy server secrets - for secret in self.secret_ids: - secret.sudo().copy({"server_id": result.id}) - - for var_value in self.variable_value_ids: - # Duplicating a server with variable values and then duplicating the - # duplicate causes a uniqueness constraint error for the 'reference' field - # in 'cx.tower.variable.value'. This happens because 'reference' is - # generated from the 'name' field, which is a related field fetching the - # same value across duplications. To avoid this, we pass the existing - # 'reference' as 'name' during duplication, ensuring unique 'reference' - # generation for each copy. - var_value.copy({"server_id": result.id, "name": var_value.reference}) - - for server_log in self.server_log_ids: - server_log.copy({"server_id": result.id}) - - return result - - # ------------------------------ - # ---- Actions - # ------------------------------ - - @check_identity - def action_show_host_key(self): - """Show host key""" - self.ensure_one() - try: - host_key = self._get_host_key_from_host() - is_error = False - except Exception as error: - is_error = True - host_key = error - context = { - "default_host_key": host_key, - "default_is_error": is_error, - "default_server_id": self.id, - } - return { - "type": "ir.actions.act_window", - "name": _("Host Key"), - "res_model": "cx.tower.server.host.key.wizard", - "view_mode": "form", - "target": "new", - "context": context, - } - - def action_update_server_logs(self): - """Update selected log from its source.""" - for server in self: - if server.server_log_ids: - server.server_log_ids.action_update_log() - - def action_open_command_logs(self): - """ - Open current server command log records - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_command_log" - ) - action["domain"] = [("server_id", "=", self.id)] # pylint: disable=no-member - return action - - def action_open_plan_logs(self): - """ - Open current server flightplan log records - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_plan_log" - ) - action["domain"] = [("server_id", "=", self.id)] # pylint: disable=no-member - return action - - def action_run_command(self): - """ - Returns wizard action to select command and run it - """ - context = self.env.context.copy() - context.update( - { - "default_server_ids": self.ids, - } - ) - return { - "type": "ir.actions.act_window", - "name": _("Run Command"), - "res_model": "cx.tower.command.run.wizard", - "view_mode": "form", - "target": "new", - "context": context, - } - - def action_run_flight_plan(self): - """ - Returns wizard action to select flightplan and run it - """ - context = self.env.context.copy() - context.update( - { - "default_server_ids": self.ids, - } - ) - return { - "type": "ir.actions.act_window", - "name": _("Run Flight Plan"), - "res_model": "cx.tower.plan.run.wizard", - "view_mode": "form", - "target": "new", - "context": context, - } - - def action_open_files(self): - """ - Open files of the current server - """ - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.cx_tower_file_action" - ) - action["domain"] = [("server_id", "=", self.id)] # pylint: disable=no-member - - context = self._context.copy() - if "context" in action and isinstance((action["context"]), str): - context.update(ast.literal_eval(action["context"])) - else: - context.update(action.get("context", {})) - - context.update( - { - "default_server_id": self.id, # pylint: disable=no-member - } - ) - action["context"] = context - return action - - # ------------------------------ - # ---- Connectivity - # ------------------------------ - - def _get_ssh_client(self, raise_on_error=False, timeout=5000, skip_host_key=False): - """Create a new SSH client instance - - Args: - raise_on_error (bool, optional): If true will raise exception - in case or error, otherwise False will be returned - Defaults to True. - timeout (int, optional): SSH connection timeout in seconds. - skip_host_key (bool, optional): If true will skip host key verification. - Defaults to False. - - Raises: - ValidationError: If the provided server reference is invalid or - the server cannot be found. - - Returns: - SSH: SSH manager instance or False and exception content - """ - self.ensure_one() - self = self.sudo() - try: - host_key = self._get_secret_value("host_key") - - # Check host only if IP address is present - skip_host_key = skip_host_key or self.skip_host_key - if ( - not host_key - and not skip_host_key - and (self.ip_v4_address or self.ip_v6_address) - ): - raise ValidationError( - _("Host key not found for server %(server)s", server=self.name) - ) - - connection = SSHConnection( - host=self.ip_v4_address or self.ip_v6_address, - port=self.ssh_port, - username=self.ssh_username, - password=self._get_ssh_password(), - ssh_key=self._get_ssh_key(), - host_key=host_key if host_key and not self.skip_host_key else None, - mode=self.ssh_auth_mode, - timeout=timeout, - ) - client = SSHManager(connection) - - except Exception as e: - if raise_on_error: - raise ValidationError(_("SSH connection error %(err)s", err=e)) from e - return False, e - return client - - def test_ssh_connection( - self, - raise_on_error=True, - return_notification=True, - try_command=True, - try_file=True, - timeout=60, - ): - """Test SSH connection. - - Args: - raise_on_error (bool, optional): Raise exception in case of error. - Defaults to True. - return_notification (bool, optional): Show sticky notification - Defaults to True. - try_command (bool, optional): Try to run a command. - Defaults to True. - try_file (bool, optional): Try file operations. - Defaults to True. - timeout (int, optional): SSH connection timeout in seconds. - Defaults to 60. - - Raises: - ValidationError: In case of SSH connection error. - ValidationError: In case of no output received. - ValidationError: In case of file operations error. - - Returns: - dict: { - "status": int, - "response": str, - "error": str, - } - """ - self.ensure_one() - client = self._get_ssh_client(raise_on_error=raise_on_error, timeout=timeout) - - if not try_command and not try_file: - try: - client.connection.connect() - return { - "status": 0, - "response": _("Connection successful."), - "error": "", - } - except Exception as e: - if raise_on_error: - raise ValidationError( - _("SSH connection error %(err)s", err=e) - ) from e - else: - return { - "status": SSH_CONNECTION_ERROR, - "response": _("Connection failed."), - "error": e, - } - - # Try command - if try_command: - command = self._get_connection_test_command() - test_result = self._run_command_using_ssh( - client, command_code=command, **{"raise_on_error": raise_on_error} - ) - status = test_result.get("status", 0) - response = test_result.get("response", "") - error = test_result.get("error", "") - - # Got an error - if raise_on_error and (status != 0 or error): - raise ValidationError( - _( - "Cannot run command\n. CODE: %(status)s. " - "RESULT: %(res)s. ERROR: %(err)s", - status=status, - res=response, - err=error, - ) - ) - - # No output received - if raise_on_error and not response: - raise ValidationError( - _( - "No output received." - " Please log in manually and check for any issues.\n" - "===\nCODE: %(status)s", - status=status, - ) - ) - - if try_file: - # test upload file - self.upload_file("test", "/tmp/cetmix_tower_test_connection.txt") - - # test download loaded file - self.download_file("/tmp/cetmix_tower_test_connection.txt") - - # remove file from server - file_test_result = self._run_command_using_ssh( - client, command_code="rm -rf /tmp/cetmix_tower_test_connection.txt" - ) - file_status = file_test_result.get("status", 0) - file_error = file_test_result.get("error", "") - - # In case of an error, raise or replace command result with file test result - if file_status != 0 or file_error: - if raise_on_error: - raise ValidationError( - _( - "Cannot remove test file using command.\n " - "CODE: %(status)s. ERROR: %(err)s", - err=file_error, - status=file_status, - ) - ) - - # Replace command result with file test result - test_result = file_test_result - - # Return notification - if return_notification: - response = test_result.get("response", "") - return self._get_notification_action( - _( - "Connection test passed! \n%(res)s", - res=response.rstrip(), - ), - notification_type="info", - title=_("Success"), - sticky=False, - ) - - return test_result - - def _get_connection_test_command(self): - """Get command used to test SSH connection - - Returns: - Char: SSH command - """ - command = "uname -a" - return command - - def _get_ssh_password(self): - """Get ssh password - This function prepares and returns ssh password for the ssh connection - Override this function to implement own password algorithms - - Returns: - Char: password ready to be used for connection parameters - """ - self.ensure_one() - password = self._get_secret_value("ssh_password") - return password - - def _get_ssh_key(self): - """Get SSH key - Get private key for an SSH connection - - Returns: - Char: SSH private key - """ - self.ensure_one() - # To ensure that key will be read - # regardless of access rights - if self.sudo().ssh_key_id: - # Use context key to read secret value - ssh_key = self.ssh_key_id._get_secret_value("secret_value") - else: - ssh_key = None - return ssh_key - - @ensure_ssh_disconnect - def _get_host_key_from_host(self, raise_on_error=False, timeout=60): - """Get host key - - Args: - raise_on_error (bool, optional): If true will raise exception - in case or error, otherwise False will be returned - Defaults to True. - timeout (int, optional): SSH connection timeout in seconds. - - Raises: - ValidationError: If the provided server reference is invalid or - the server cannot be found. - - Returns: - Host key: Host key of the server - """ - self.ensure_one() - - # Check access before getting host key - # This is needed to avoid possible access violations - self.check_access_rights("read") - self.check_access_rule("read") - - try: - # Skip host key verification to obtain the server's real host key. - client = self._get_ssh_client( - raise_on_error=raise_on_error, timeout=timeout, skip_host_key=True - ) - - # Disable host key verification for this connection only, to obtain the - # server's real host key. If a pre-configured host key is incorrect using - # it would cause a key mismatch error. By setting host_key to False - # here, we trigger AutoAddPolicy for this connection, which automatically - # accepts the server's actual host key. - client.connection.host_key = False - - ssh_client = client.connection.connect() - transport = ssh_client.get_transport() - remote_key = transport.get_remote_server_key() - host_key = remote_key.get_base64() - return host_key - except Exception as e: - if raise_on_error: - raise ValidationError( - _("Error retrieving host key: %(err)s", err=e) - ) from e - else: - return None - - # ------------------------------ - # ---- Command execution - # ------------------------------ - - def run_command(self, command, path=None, sudo=None, ssh_connection=None, **kwargs): - """This is the main function to use for running commands. - It renders command code, creates log record and calls command runner. - - Args: - command (cx.tower.command()): Command record - path (Char): directory where command is run. - Provide in case you need to override default command value - sudo (Boolean): use sudo - Defaults to None - ssh_connection (SSH client instance, optional): SSH connection. - Pass to reuse existing connection. - This is useful in case you would like to speed up - the ssh command running. - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log", dict(): values passed to logger - - "key", dict(): values passed to key parser - - "variable_values", dict(): custom variable values - in the format of `{variable_reference: variable_value}` - eg `{'odoo_version': '16.0'}` - Will be applied only if user has write access to the server. - Context: - no_command_log (Bool): set this context key to `True` - to disable log creation. - Command running results will be returned instead. - If any non command related error occurs in the command running flow - an exception will be raised. - IMPORTANT: be aware when running commands with `no_command_log=True` - because no `Allow Parallel Run` check will be done! - Returns: - dict(): command running result if `no_command_log` - context value == True else None - """ - self.ensure_one() - - # Populate `sudo` value from the server settings if not provided explicitly - if self.sudo().ssh_username == "root": - sudo = False - elif sudo is None or sudo: - sudo = self.sudo().use_sudo - - # Prepare log object - log_obj = self.env["cx.tower.command.log"] - log_vals = kwargs.get("log", {}) - log_vals.update( - {"use_sudo": sudo, "variable_values": kwargs.get("variable_values", {})} - ) - - # Check if no log record should be created - no_command_log = self._context.get("no_command_log") - - # Check if command can be run on this server: - # 1. Server is listed in command's server_ids - # 2. There are no server_ids at all (command is not server specific) - if not command._check_server_compatibility(self): - error = _("Command is not compatible with the server") - if no_command_log: - return { - "status": COMMAND_NOT_COMPATIBLE_WITH_SERVER, - "response": None, - "error": error, - } - log_obj.record( - server_id=self.id, # pylint: disable=no-member - command_id=command.id, - status=COMMAND_NOT_COMPATIBLE_WITH_SERVER, - error=error, - **log_vals, - ) - return - - # Check if another instance of the same command is running - another_command_running = ( - not command.allow_parallel_run - and log_obj.sudo().search_count( - [ - ("server_id", "=", self.id), # pylint: disable=no-member - ("command_id", "=", command.id), - ("is_running", "=", True), - ] - ) - ) - - # Another command is running, return error - if another_command_running: - if no_command_log: - return { - "status": ANOTHER_COMMAND_RUNNING, - "response": None, - "error": _("Another instance of the command is already running"), - } - log_obj.record( - server_id=self.id, # pylint: disable=no-member - command_id=command.id, - status=ANOTHER_COMMAND_RUNNING, - error=_("Another instance of the command is already running"), - **log_vals, - ) - return - - # Render command - custom_variable_values = kwargs.get("variable_values", {}) - rendered_command = self._render_command(command, path, custom_variable_values) - rendered_command_code = rendered_command["rendered_code"] - rendered_command_path = rendered_command["rendered_path"] - - # Prepare key renderer values - key_vals = kwargs.get("key", {}) # Get vals from kwargs - key_vals.update({"server_id": self.id}) # pylint: disable=no-member - if self.partner_id: - key_vals.update({"partner_id": self.partner_id.id}) - kwargs.update({"key": key_vals}) - - # Save rendered code to log - if no_command_log: - log_record = None - else: - log_vals.update( - {"code": rendered_command_code, "path": rendered_command_path} - ) - # Create log record - log_record = log_obj.start(self.id, command.id, **log_vals) # pylint: disable=no-member - # If on command we have the flag - if command.no_split_for_sudo: - kwargs["no_split_for_sudo"] = True - return self._command_runner_wrapper( - command=command, - log_record=log_record, - rendered_command_code=rendered_command_code, - sudo=sudo, - rendered_command_path=rendered_command_path, - ssh_connection=ssh_connection, - **kwargs, - ) - - def _render_command(self, command, path=None, custom_variable_values=None): - """Renders command code for selected command for current server - - Args: - command (cx.tower.command): Command to render - path (Char): Path where to run the command. - Provide in case you need to override default command path - - Returns: - dict: rendered values - { - "rendered_code": rendered command code, - "rendered_path": rendered command path - } - """ - self.ensure_one() - - variables = [] - - # Get variables from code - if command.code: - variables_extracted = command.get_variables_from_code(command.code) - for ve in variables_extracted: - if ve not in variables: - variables.append(ve) - - # Get variables from path - path = path if path else command.path - if path: - variables_extracted = command.get_variables_from_code(path) - for ve in variables_extracted: - if ve not in variables: - variables.append(ve) - - # Get variable values for current server - variable_values_dict = ( - self.sudo().get_variable_values(variables) # pylint: disable=no-member - if variables - else False - ) - - # Extract variable values for current server - variable_values = ( - variable_values_dict.get(self.id) if variable_values_dict else {} - ) # pylint: disable=no-member - - # Apply custom variable values only if user has write access to the server - has_write_access = self._have_access_to_server("write") - if custom_variable_values and has_write_access: - variable_values.update(custom_variable_values) - - # Render command code and path using variables - if variable_values: - if command.action == "python_code": - variable_values["pythonic_mode"] = True - - rendered_code = ( - command.render_code_custom(command.code, **variable_values) - if command.code - else False - ) - rendered_path = ( - command.render_code_custom(path, **variable_values) if path else False - ) - - else: - rendered_code = command.code - rendered_path = path - - return {"rendered_code": rendered_code, "rendered_path": rendered_path} - - def _have_access_to_server(self, operation): - """Check access to the server. - This is a wrapper function over the Odoo built-in ones. - It's used in order we need to implement custom access checks. - - Args: - operation (Char): Operation to check access - same format as `check_access_rights` - Returns: - Bool: True if access is granted, False otherwise - """ - # Check access rights first - has_write_access = self.check_access_rights(operation, raise_exception=False) - - # Check access rule to parti - if has_write_access: - try: - self.check_access_rule(operation) - except UserError: - has_write_access = False - return has_write_access - - def run_flight_plan(self, flight_plan, **kwargs): - """ - Runs flight plan on the current server. - - Args: - flight_plan (cx.tower.plan()): flight plan record - kwargs (dict): Optional arguments - Following are supported but not limited to: - - "plan_log": {values passed to flightplan logger} - - "log": {values passed to logger} - - "key": {values passed to key parser} - - "variable_values", dict(): custom variable values - in the format of `{variable_reference: variable_value}` - eg `{'odoo_version': '16.0'}` - Will be applied only if user has write access to the server. - """ - - self.ensure_one() - - # Run flight plan - return flight_plan._run_single(self, **kwargs) - - def _command_runner_wrapper( - self, - command, - log_record, - rendered_command_code, - sudo=None, - rendered_command_path=None, - ssh_connection=None, - **kwargs, - ): - """Used to implement custom runner mechanisms. - Use it in case you need to redefine the entire command running engine. - Eg it's used in `cetmix_tower_server_queue` OCA `queue_job` implementation. - - Args: - command (cx.tower.command()): Command - log_record (cx.tower.command.log()): Command log record - rendered_command_code (Text): Rendered command code. - We are passing in case it differs from command code in the log record. - sudo (Selection): Command sudo mode. Defaults to None. - rendered_command_path (Char, optional): Rendered command path. - ssh_connection (SSH client instance, optional): SSH connection to reuse. - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log": {values passed to logger} - - "key": {values passed to key parser} - - Context: - use_sudo (Bool): use sudo for command running - - Returns: - dict(): command running result if `log_record` is defined else None - """ - return self._command_runner( - command=command, - log_record=log_record, - rendered_command_code=rendered_command_code, - sudo=sudo, - rendered_command_path=rendered_command_path, - ssh_connection=ssh_connection, - **kwargs, - ) - - def _command_runner( - self, - command, - log_record, - rendered_command_code, - sudo=None, - rendered_command_path=None, - ssh_connection=None, - **kwargs, - ): - """Top level command runner function. - Calls command type specific runners. - - Args: - command (cx.tower.command()): Command - log_record (cx.tower.command.log()): Command log record - rendered_command_code (Text): Rendered command code. - We are passing in case it differs from command code in the log record. - sudo (Selection): Command sudo mode. Defaults to None. - rendered_command_path (Char, optional): Rendered command path. - ssh_connection (SSH client instance, optional): SSH connection to reuse. - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log": {values passed to logger} - - "key": {values passed to key parser} - Returns: - dict(): command running result if `log_record` is defined else None - """ - response = None - need_check_server_status = True - if command.action == "ssh_command": - response = self._command_runner_ssh( - log_record=log_record, - rendered_command_code=rendered_command_code, - sudo=sudo, - rendered_command_path=rendered_command_path, - ssh_connection=ssh_connection, - **kwargs, - ) - elif command.action == "file_using_template": - response = self._command_runner_file_using_template( - log_record, - rendered_command_path, - **kwargs, - ) - elif command.action == "python_code": - response = self._command_runner_python_code( - log_record, - rendered_command_code, - **kwargs, - ) - elif command.action == "plan": - response = self.with_context( - prevent_plan_recursion=True - )._command_runner_flight_plan( - log_record, - command.flight_plan_id, - **kwargs, - ) - need_check_server_status = True - else: - need_check_server_status = False - - if ( - need_check_server_status - and command.server_status - and ( - (log_record and log_record.command_status == 0) - or (response and response["status"] == 0) - ) - ): - self.write({"status": command.server_status}) - - if need_check_server_status: - return response - - error_message = _( - "No runner found for command action '%(cmd_action)s'", - cmd_action=command.action, - ) - if log_record: - log_record.finish( - finish_date=fields.Datetime.now(), - status=NO_COMMAND_RUNNER_FOUND, - response=None, - error=error_message, - ) - else: - raise ValidationError(error_message) - - def _command_runner_file_using_template_create_file( - self, file_template_id, server_dir, plan_line, if_file_exists, **kwargs - ): - """ - Creates a file on the server using the specified file template. - - This method is intended to allow overriding the file creation logic - and provides access to the created file object. - Args: - file_template_id (recordset): The file template to use for creating - the new file. - server_dir (str): The directory on the server where the file should be - created. - plan_line (recordset): The plan line to use for creating - the new file. - if_file_exists (str): The action to take if the file already exists. - **kwargs: Additional keyword arguments. - - Returns: - record: The created file record. - """ - return file_template_id.create_file( - server=self, - server_dir=server_dir, - if_file_exists=if_file_exists, - ) - - def _command_runner_file_using_template( - self, - log_record, - server_dir, - **kwargs, - ): - """ - Run the command to create a file from a template and push to server if source - is 'tower' and pull to tower if source is 'server'. - - This function attempts to create a new file on the server/tower using the - specified file template. If the file creation is successful, it uploads - the file to the server/tower. The function logs the status of the operation - in the provided log record. - - Args: - log_record (recordset): The log record to update with the command's - status. - server_dir (str): The directory on the server where the file should be - created. - **kwargs: Additional keyword arguments. - - Returns: - None - - Raises: - Exception: If any error occurs during the file creation or upload - process, it logs the error and the exception message in the - log record. - """ - try: - # Attempt to create a new file using the template for the current server - plan_line = log_record.plan_log_id.plan_line_executed_id - file_template_id = log_record.command_id.file_template_id - file = self._command_runner_file_using_template_create_file( - file_template_id=file_template_id, - if_file_exists=log_record.command_id.if_file_exists, - server_dir=server_dir, - plan_line=plan_line, - ) - - # If file creation failed, log the failure and exit - if not file: - command_result = { - "status": FILE_CREATION_FAILED, - "response": None, - "error": _("File already exists"), - } - if log_record: - return log_record.finish( - finish_date=fields.Datetime.now(), - status=command_result["status"], - response=command_result["response"], - error=command_result["error"], - ) - else: - return command_result - - # Context is used to detect a retry - # and avoid handling skip logic on first attempt - is_creation_skipped = file._context.get("file_creation_skipped") - - if not is_creation_skipped: - if file.source == "server": - file.action_pull_from_server() - elif file.source == "tower": - file.action_push_to_server() - else: - raise UserError( - _( - "File source cannot be determined: '%(source)s'", - source=file.source, - ) - ) - - if log_record.command_id.disconnect_file: - file.action_unlink_from_template() - - if is_creation_skipped: - return log_record.finish( - fields.Datetime.now(), - 0, - _("File already exists on server. Upload skipped"), - None, - ) - - # Log the successful creation and upload of the file - return log_record.finish( - finish_date=fields.Datetime.now(), - status=0, - response=_("File created and uploaded successfully"), - error=None, - ) - - except Exception as e: - # Log any exception that occurs during the process - log_record.finish( - finish_date=fields.Datetime.now(), - status=FILE_CREATION_FAILED, - response=None, - error=_("An error occurred: %(error)s", error=str(e)), - ) - - def _command_runner_ssh( - self, - log_record, - rendered_command_code, - sudo=None, - rendered_command_path=None, - ssh_connection=None, - **kwargs, - ): - """Run SSH command. - Updates the record in the Command Log (cx.tower.command.log) - - Args: - log_record (cx.tower.command.log()): Command log record - rendered_command_code (Text): Rendered command code. - We are passing in case it differs from command code in the log record. - sudo (Selection): Command sudo mode. Defaults to None. - rendered_command_path (Char, optional): Rendered command path. - ssh_connection (SSH client instance, optional): SSH connection to reuse. - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log": {values passed to logger} - - "key": {values passed to key parser} - - "raise_on_error": Raise exception on error. - - Returns: - dict(): command running result if `log_record` is defined else None - """ - raise_on_error = kwargs.get("raise_on_error", False) - if not ssh_connection: - ssh_connection = self._get_ssh_client(raise_on_error=raise_on_error) - - # Run command - command_result = self._run_command_using_ssh( - client=ssh_connection, - command_code=rendered_command_code, - command_path=rendered_command_path, - raise_on_error=raise_on_error, - sudo=sudo, - **kwargs, - ) - - # Log result - if log_record: - log_record.finish( - finish_date=fields.Datetime.now(), - status=command_result["status"], - response=command_result["response"], - error=command_result["error"], - ) - else: - return command_result - - def _command_runner_flight_plan( - self, log_record, flight_plan, raise_on_error=False, **kwargs - ): - """ - Run Flight plan from command. - Updates the record in the Command Log (cx.tower.command.log) - Args: - log_record (cx.tower.command.log()): Command log record. - flight_plan (cx.tower.plan()): Flight Plan to be run. - raise_on_error (bool, optional): raise error on error. - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log": {values passed to logger} - - "key": {values passed to key parser} - Returns: - dict(): flight plan running result if `log_record` is - not defined else None - """ - response = None - error = None - status = 0 - try: - # Generate custom label and add values for log - kwargs["plan_log"] = { - "label": generate_random_id(4), - "parent_flight_plan_log_id": log_record.plan_log_id.id, - } - # add executed command with action "plan" to save link to plan log - kwargs["flight_plan_command_log"] = log_record - plan_log_record = flight_plan.with_context(from_command=True)._run_single( - self, **kwargs - ) - except Exception as e: - if raise_on_error: - raise ValidationError( - _("Flight plan running error %(err)s", err=e) - ) from e - else: - status = GENERAL_ERROR - error = e - else: - if plan_log_record.plan_status != 0: - status = plan_log_record.plan_status - error = _("Flight plan running error") - - result = {"status": status, "response": response, "error": error} - if log_record: - log_record.finish( - finish_date=fields.Datetime.now(), - status=result["status"], - response=result["response"], - error=result["error"], - variable_values=plan_log_record.variable_values, - ) - else: - return result - - def _command_runner_python_code( - self, - log_record, - rendered_code, - **kwargs, - ): - """ - Run Python code. - Updates the record in the Command Log (cx.tower.command.log) - - Args: - log_record (cx.tower.command.log()): Command log record - rendered_code (Text): Rendered python code. - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log": {values passed to logger} - - "key": {values passed to key parser} - - Returns: - dict(): python code running result if `log_record` is - not defined else None - """ - # Run python code - result = self._run_python_code( - code=rendered_code, - raise_on_error=False, - **kwargs, - ) - - # Log result - if log_record: - log_record.finish( - finish_date=fields.Datetime.now(), - status=result["status"], - response=result["response"], - error=result["error"], - variable_values=result["variable_values"], - ) - else: - return result - - @ensure_ssh_disconnect - def _run_command_using_ssh( - self, - client, - command_code, - command_path=None, - raise_on_error=False, - sudo=None, - **kwargs, - ): - """This is a low level method for running an SSH command. - Use it in case you need to get direct output of an SSH command. - Otherwise call `run_command()` - - Args: - client (Connection): valid server ssh connection object - command_code (Text): command text - command_path (Char, optional): directory where command should be run - raise_on_error (bool, optional): raise error on error - sudo (Selection): Command sudo mode. Defaults to None. Defaults to None. - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log": {values passed to logger} - - "key": {values passed to key parser} - - Raises: - ValidationError: if client is not valid - ValidationError: command run error - - Returns: - dict: { - "status": , - "response": Text, - "error": Text - } - """ - if not client: - if raise_on_error: - raise ValidationError(_("SSH Client is not defined.")) - return { - "status": SSH_CONNECTION_ERROR, - "response": False, - "error": _("SSH Client is not defined."), - } - - # Client contains a result of _get_ssh_client() - # If it's a tuple, it means there was an error getting the client - if isinstance(client, tuple): - error = client[1] - if raise_on_error: - raise ValidationError(error) - return { - "status": SSH_CONNECTION_ERROR, - "response": False, - "error": error, - } - - # Parse inline secrets - code_and_secrets = self.env["cx.tower.key"]._parse_code_and_return_key_values( - command_code, **kwargs.get("key", {}) - ) - command_code = code_and_secrets["code"] - secrets = code_and_secrets["key_values"] - - # Prepare ssh command - prepared_command_code = self._prepare_ssh_command( - command_code, - command_path, - sudo, - **kwargs, - ) - - try: - status = [] - response = [] - error = [] - - # Command is a single sting. No 'sudo' or 'sudo' w/o password - if isinstance(prepared_command_code, str): - status, response, error = client.command_executor.exec_command( - prepared_command_code, sudo=sudo - ) - - # Multiple commands: sudo with password - elif isinstance(prepared_command_code, list): - for cmd in prepared_command_code: - st, resp, err = client.command_executor.exec_command(cmd, sudo=sudo) - status.append(st) - response += resp - error += err - - # Something weird )) - else: - status = GENERAL_ERROR - - except Exception as e: - if raise_on_error: - _logger.error(f"SSH run command error: {e}") - raise ValidationError(_("SSH run command error %(err)s", err=e)) from e - status = GENERAL_ERROR - response = [] - error = [e] - - result = self._parse_command_results(status, response, error, secrets, **kwargs) - return result - - def _run_python_code( - self, - code, - raise_on_error=False, - **kwargs, - ): - """ - This is a low level method for Python code running. - - Args: - code (Text): python code - raise_on_error (bool, optional): raise error on error - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log": {values passed to logger} - - "key": {values passed to key parser} - - Raises: - ValidationError: python code running error - - Returns: - dict: { - "status": , - "response": Text, - "error": Text - } - """ - response = None - error = None - status = 0 - secrets = None - - try: - # Parse inline secrets - code_and_secrets = self.env[ - "cx.tower.key" - ]._parse_code_and_return_key_values( - code, pythonic_mode=True, **kwargs.get("key", {}) - ) - secrets = code_and_secrets.get("key_values") - command_code = code_and_secrets["code"] - - code = self.env["cx.tower.key"]._parse_code( - command_code, pythonic_mode=True, **kwargs.get("key", {}) - ) - - # Get the evaluation context for the python command - eval_context = self.env[ - "cx.tower.command" - ]._get_python_command_eval_context( - server=self, variable_values=kwargs.get("variable_values", {}) - ) - - safe_eval( - code, - eval_context, - mode="exec", - nocopy=True, - ) - kwargs["variable_values"] = eval_context.get("custom_values", {}) - result = eval_context.get("result") - if result: - status = result.get("exit_code", 0) - if status == 0: - response = [result.get("message")] - else: - error = [result.get("message")] - - except Exception as e: - if raise_on_error: - raise ValidationError( - _("Python code running error: %(err)s", err=e) - ) from e - else: - status = PYTHON_COMMAND_ERROR - error = [e] - - result = self._parse_command_results(status, response, error, secrets, **kwargs) - result["variable_values"] = kwargs.get("variable_values", {}) - return result - - def _prepare_ssh_command(self, command_code, path=None, sudo=None, **kwargs): - """Prepare ssh command - IMPORTANT: - Commands run with sudo will be run separately one after another - even if there is a single command separated with '&&' - Examples: - # Default (sudo with splitting): - "pwd && ls -l" becomes: - sudo pwd - sudo ls -l - - # With no_split_for_sudo=True: - sudo pwd && ls -l - - Args: - command_code (str): initial command - path (str, optional): directory where command should be run - sudo (str, optional): sudo mode ('n' or 'p') - 'n' — sudo without password - 'p' — sudo with password - kwargs (dict): extra arguments. Supported keys: - - "log": values passed to logger - - "key": values passed to key parser - - "no_split_for_sudo" (bool): if True, do not split on '&&' - - Returns: - list or str: if sudo='p' (with password), returns a list of commands; - if sudo='n', returns a single string (possibly joined by '&&'); - without sudo, returns the raw command_code. - """ - # Prepare command for sudo if needed - if sudo: - # Add location - sudo_prefix = "sudo -S -p ''" - - no_split = kwargs.get("no_split_for_sudo", False) - - separator = "&&" - # split only when '&&' is present AND splitting is not disabled - if separator in command_code and not no_split: - result = ( - command_code.replace("\\", "").replace("\n", "").split(separator) - ) - - # Sudo with password expects a list of commands - result = [f"{sudo_prefix} {cmd.strip()}" for cmd in result] - - # Merge back into a single command is sudo is without password - if sudo == "n": - result = f" {separator} ".join(result) - else: - # single command or no_split requested - result = f"{sudo_prefix} {command_code}" - # Sudo with password expects a list of commands - if sudo == "p": - result = [result] - else: - # Command without sudo is always run as is - result = command_code - # Add path change command - if path: - # Add sudo prefix if needed - cd_command = f"cd {path}" - - if isinstance(result, list): - result = [cd_command] + result - else: - result = f"{cd_command} && {result}" - - return result - - def _parse_command_results( - self, status, response, error, key_values=None, **kwargs - ): - """ - Parse results of a command run with sudo (either SSH or Python). - Removes secrets and formats the response and error messages. - - Paramiko returns SSH response and error as list. - When running a command with sudo with password we return status as a list too. - _ - - Args: - status (Int or list of int): Status or statuses - response (list): Response - error (list): Error - key_values (list): Secrets that were discovered in code. - Used to clean up command result. - kwargs (dict): extra arguments. Use to pass external values. - Following keys are supported by default: - - "log": {values passed to logger} - - "key": {values passed to key parser} - - Returns: - dict: { - "status": , - "response": , - "error": - } - """ - - # In case of several statuses we return the last one that is not 0 ("ok") - # Eg for [0,1,0,4,0] result will be 4 - if isinstance(status, list): - final_status = 0 - for st in status: - if st != 0 and st != status: - final_status = st - - status = final_status - - # This is needed to remove keys - if key_values: - key_model = self.env["cx.tower.key"] - - # Compose response message - if response and isinstance(response, list): - # Replace secrets with spoiler - response_vals = [ - key_model._replace_with_spoiler(str(r), key_values) - if key_values - else str(r) - for r in response - ] - response = "".join(response_vals) - - elif not response: - # For not to save an empty list `[]` in log - response = None - - # Compose error message - if error and isinstance(error, list): - # Replace secrets with spoiler - error_vals = [ - key_model._replace_with_spoiler(str(e), key_values) - if key_values - else str(e) - for e in error - ] - error = "".join(error_vals) - elif not error: - # For not to save an empty list `[]` in log - error = None - - return { - "status": status, - "response": response, - "error": error, - } - - def _check_zombie_commands(self): - """ - Check commands that are running longer than the timeout - and mark them as finished - """ - timeout = int( - self.env["ir.config_parameter"] - .sudo() - .get_param("cetmix_tower_server.command_timeout", 0) - ) - if not timeout: - return - - # SSH or Python command is running longer than the timeout - # We are not terminating Flight Plans and File Upload commands - domain = [ - ("is_running", "=", True), - ("start_date", "<", fields.Datetime.now() - timedelta(seconds=timeout)), - ("command_action", "in", ["ssh_command", "python_code"]), - ] - zombie_command_logs = self.env["cx.tower.command.log"].search(domain) - if zombie_command_logs: - zombie_command_logs.finish( - status=COMMAND_TIMED_OUT, - response=None, - error=COMMAND_TIMED_OUT_MESSAGE, - ) - - # ------------------------------ - # ---- File management - # ------------------------------ - - @ensure_ssh_disconnect - def delete_file(self, remote_path): - """ - Delete file from remote server - - Args: - remote_path (Text): full path file location with file type - (e.g. /test/my_file.txt). - """ - self.ensure_one() - client = self._get_ssh_client(raise_on_error=True) - client.sftp_service.delete_file(remote_path) - - @ensure_ssh_disconnect - def upload_file(self, data, remote_path, from_path=False): - """ - Upload file to remote server. - - Args: - data (Text, Bytes): If the data are text, they are converted to bytes, - contains a local file path if from_path=True. - remote_path (Text): full path file location with file type - (e.g. /test/my_file.txt). - from_path (Boolean): set True if `data` is file path. - - Raise: - TypeError: incorrect type of file. - - Returns: - Result (class paramiko.sftp_attr.SFTPAttributes): metadata of the - uploaded file. - """ - self.ensure_one() - client = self._get_ssh_client(raise_on_error=True) - if from_path: - result = client.sftp_service.upload_file(data, remote_path) - else: - # Convert string to bytes - if isinstance(data, str): - data = data.encode() - file = io.BytesIO(data) - result = client.sftp_service.upload_file(file, remote_path) - - return result - - @ensure_ssh_disconnect - def download_file(self, remote_path): - """ - Download file from remote server - - Args: - remote_path (Text): full path file location with file type - (e.g. /test/my_file.txt). - - Raise: - ValidationError: raise if file not found. - - Returns: - Result (Bytes): file content. - """ - self.ensure_one() - client = self._get_ssh_client(raise_on_error=True) - try: - result = client.sftp_service.download_file(remote_path) - - except FileNotFoundError as fe: - raise ValidationError( - _("The file %(f_path)s not found.", f_path=remote_path) - ) from fe - return result - - # ------------------------------ - # ---- Auxiliary functions - # ------------------------------ - - def server_toggle_active(self, self_active): - """ - Change active status of related records - - Args: - self_active (bool): active status of the record - """ - self.file_ids.filtered(lambda f: f.active == self_active).toggle_active() - self.command_log_ids.filtered(lambda c: c.active == self_active).toggle_active() - self.plan_log_ids.filtered(lambda p: p.active == self_active).toggle_active() - self.variable_value_ids.filtered( - lambda vv: vv.active == self_active - ).toggle_active() - - def toggle_active(self): - """Archiving related server""" - res = super().toggle_active() - server_active = self.with_context(active_test=False).filtered( - lambda x: x.active - ) - server_not_active = self - server_active - if server_active: - server_active.server_toggle_active(False) - if server_not_active: - server_not_active.server_toggle_active(True) - return res - - def _is_being_deleted(self): - """Check if the server is being deleted. - - Returns: - bool: True if the server is being deleted, False otherwise - """ - self.ensure_one() - return self.status and self.status == "deleting" - - def _get_post_create_fields(self): - """ - Add fields that should be populated after server creation - """ - res = super()._get_post_create_fields() - return res + ["variable_value_ids", "server_log_ids", "secret_ids"] - - def _get_notification_action( - self, message, notification_type="info", title=None, sticky=True - ): - """Get notification action - - Args: - message (str): Message - notification_type (str, optional): Notification type. Defaults to "info". - title (str, optional): Title. Defaults to None. - sticky (bool, optional): Sticky notification. Defaults to True. - - Returns: - dict: Notification action - """ - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "type": notification_type, - "title": title, - "message": message, - "sticky": sticky, - }, - } - - def _get_dependent_model_relation_fields(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_dependent_model_relation_fields() - return res + ["variable_value_ids", "file_ids"] diff --git a/addons/cetmix_tower_server/models/cx_tower_server_log.py b/addons/cetmix_tower_server/models/cx_tower_server_log.py deleted file mode 100644 index 8348380..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_server_log.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging - -from ansi2html import Ansi2HTMLConverter - -from odoo import _, api, fields, models -from odoo.exceptions import AccessError - -_logger = logging.getLogger(__name__) - -html_converter = Ansi2HTMLConverter(inline=True) - - -class CxTowerServerLog(models.Model): - """Server log management. - Used to track various server logs. - N.B. Do not mistake for command of flight plan log! - """ - - _name = "cx.tower.server.log" - _inherit = ["cx.tower.access.mixin", "cx.tower.reference.mixin"] - _description = "Cetmix Tower Server Log" - - NO_LOG_FETCHED_MESSAGE = _("") - - active = fields.Boolean(default=True) - server_id = fields.Many2one("cx.tower.server", ondelete="cascade") - log_type = fields.Selection( - selection=lambda self: self._selection_log_type(), - required=True, - groups="cetmix_tower_server.group_root,cetmix_tower_server.group_manager", - default=lambda self: self._selection_log_type()[0][0], - ) - command_id = fields.Many2one( - "cx.tower.command", - domain="[('action', 'in', ['ssh_command', 'python_code']), " - "'|', ('server_ids', 'in', [server_id]), ('server_ids', '=', False)]", - groups="cetmix_tower_server.group_root,cetmix_tower_server.group_manager", - help="Command that will be executed to get the log data.\n" - "Be careful with commands that don't support parallel execution!", - ) - use_sudo = fields.Boolean( - groups="cetmix_tower_server.group_root,cetmix_tower_server.group_manager", - help="Will use sudo based on server settings." - "If no sudo is configured will run without sudo", - ) - file_id = fields.Many2one( - "cx.tower.file", - domain="[('server_id', '=', server_id)]", - groups="cetmix_tower_server.group_root,cetmix_tower_server.group_manager", - help="File that will be executed to get the log data", - ) - log_text = fields.Text(readonly=True, copy=False) - log_html = fields.Html(compute="_compute_log_html") - - # --- Server template related - server_template_id = fields.Many2one("cx.tower.server.template", ondelete="cascade") - file_template_id = fields.Many2one( - "cx.tower.file.template", - ondelete="cascade", - groups="cetmix_tower_server.group_root,cetmix_tower_server.group_manager", - help="This file template will be used to create log files" - " when server is created from a template", - ) - - def _selection_log_type(self): - """Actions that can be run by a command. - - Returns: - List of tuples: available options. - """ - return [ - ("command", "Command"), - ("file", "File"), - ] - - @api.depends("log_text") - def _compute_log_html(self): - for record in self: - if record.log_text: - try: - record.log_html = html_converter.convert(record.log_text) - # We catch all exceptions to avoid breaking the log display - except Exception as e: - _logger.error("Error converting log text to HTML: %s", e) - record.log_html = False - else: - record.log_html = False - - def copy(self, default=None): - return super( - CxTowerServerLog, self.with_context(reference_mixin_skip_self=True) - ).copy(default) - - def action_open_log(self): - """ - Open log record in current window - """ - self.ensure_one() - self.action_update_log() - return { - "type": "ir.actions.act_window", - "name": self.name, - "res_model": "cx.tower.server.log", - "res_id": self.id, # pylint: disable=no-member - "view_mode": "form", - "target": "current", - } - - def write(self, vals): - """Override to protect log_text from direct modifications. - Bypass with context key 'cx_allow_log_text_update' for internal updates. - """ - if "log_text" in vals and not self.env.context.get("cx_allow_log_text_update"): - raise AccessError(_("You are not allowed to modify the server log output.")) - return super().write(vals) - - def action_update_log(self): - """Update log text from source""" - - # We are using `sudo` to override command/file access limitations - for rec in self.sudo().with_context(cx_allow_log_text_update=True): - rec.log_text = rec._get_formatted_log_text() - - def _get_log_text(self): - """ - Get log text from source - Use this function to get pure log text from source. - - Returns: - Text: log text - """ - self.ensure_one() - if self.log_type == "file" and self.file_id: - return self._get_log_from_file() - elif self.log_type == "command" and self.command_id: - return self._get_log_from_command() - - def _get_formatted_log_text(self): - """ - Get formatted log text. - Use this function to get formatted log text. - - Returns: - Text: formatted log text - """ - log_text = self._get_log_text() - if log_text: - return self._format_log_text(log_text) - return self.NO_LOG_FETCHED_MESSAGE - - def _format_log_text(self, log_text): - """ - Format log text. - Use this function to format log text. - - Returns: - Text: formatted log text - """ - return log_text - - def _get_log_from_file(self): - """Get log from a file. - Override this function to implement custom log handler - - Returns: - Text: log text - """ - self.ensure_one() - if self.file_id.source == "server": - return self.file_id.code - if self.file_id.source == "tower": - return self.file_id.code_on_server - - def _get_log_from_command(self): - """Get log from a command. - Returns: - Text: log text - """ - self.ensure_one() - - use_sudo = self.use_sudo and self.server_id.use_sudo - command_result = self.server_id.with_context(no_command_log=True).run_command( - self.command_id, sudo=use_sudo - ) - log_text = self.NO_LOG_FETCHED_MESSAGE - if command_result: - response = command_result["response"] - error = command_result["error"] - if response: - log_text = response - elif error: - log_text = error - return log_text - - def _get_copied_name(self, force_name=None): - # Original name is preserved when log is duplicated - return force_name or self.name diff --git a/addons/cetmix_tower_server/models/cx_tower_server_template.py b/addons/cetmix_tower_server/models/cx_tower_server_template.py deleted file mode 100644 index 292dfee..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_server_template.py +++ /dev/null @@ -1,653 +0,0 @@ -# Copyright (C) 2024 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError - - -class CxTowerServerTemplate(models.Model): - """Server Template. Used to simplify server creation""" - - _name = "cx.tower.server.template" - _inherit = [ - "cx.tower.reference.mixin", - "mail.thread", - "mail.activity.mixin", - "cx.tower.access.role.mixin", - "cx.tower.tag.mixin", - ] - _description = "Cetmix Tower Server Template" - _order = "name" - - active = fields.Boolean(default=True) - - # --- Connection - ssh_port = fields.Integer(string="SSH port", default=22) - ssh_username = fields.Char(string="SSH Username") - ssh_password = fields.Char(string="SSH Password") - ssh_key_id = fields.Many2one( - comodel_name="cx.tower.key", - string="SSH Private Key", - domain=[("key_type", "=", "k")], - ) - ssh_auth_mode = fields.Selection( - string="SSH Auth Mode", - selection=[ - ("p", "Password"), - ("k", "Key"), - ], - ) - use_sudo = fields.Selection( - string="Use sudo", - selection=[("n", "Without password"), ("p", "With password")], - help="Run commands using 'sudo'", - ) - - # --- Attributes - color = fields.Integer(help="For better visualization in views") - os_id = fields.Many2one(string="Operating System", comodel_name="cx.tower.os") - tag_ids = fields.Many2many( - relation="cx_tower_server_template_tag_rel", - column1="server_template_id", - column2="tag_id", - ) - - # --- Variables - # We are not using variable mixin because we don't need to parse values - variable_value_ids = fields.One2many( - string="Variable Values", - comodel_name="cx.tower.variable.value", - auto_join=True, - inverse_name="server_template_id", - ) - - # --- Server logs - server_log_ids = fields.One2many( - comodel_name="cx.tower.server.log", inverse_name="server_template_id" - ) - - # --- Shortcuts - shortcut_ids = fields.Many2many( - comodel_name="cx.tower.shortcut", - relation="cx_tower_server_template_shortcut_rel", - column1="server_template_id", - column2="shortcut_id", - string="Shortcuts", - ) - - # --- Scheduled Tasks - scheduled_task_ids = fields.Many2many( - comodel_name="cx.tower.scheduled.task", - relation="cx_tower_server_template_scheduled_task_rel", - column1="server_template_id", - column2="scheduled_task_id", - string="Scheduled Tasks", - ) - - # --- Flight Plan - flight_plan_id = fields.Many2one( - "cx.tower.plan", - help="This flight plan will be run upon server creation", - domain="[('server_ids', '=', False)]", - ) - - # ---- Delete plan - plan_delete_id = fields.Many2one( - "cx.tower.plan", - string="On Delete Plan", - groups="cetmix_tower_server.group_manager", - help="This Flightplan will be executed when the server is deleted", - ) - - # --- Created Servers - server_ids = fields.One2many( - comodel_name="cx.tower.server", - inverse_name="server_template_id", - ) - server_count = fields.Integer( - compute="_compute_server_count", - ) - - # -- Other - note = fields.Text() - - # ---- Access. Add relation for mixin fields - user_ids = fields.Many2many( - relation="cx_tower_server_template_user_rel", - domain=lambda self: [ - ("groups_id", "in", [self.env.ref("cetmix_tower_server.group_manager").id]) - ], - ) - manager_ids = fields.Many2many( - relation="cx_tower_server_template_manager_rel", - ) - - @api.depends("server_ids") - def _compute_server_count(self): - """ - Compute total server counts created from the templates - """ - for template in self: - template.server_count = len(template.server_ids) - - def copy(self, default=None): - """Duplicate the server template along with variable values and server logs.""" - default = dict(default or {}) - - # Duplicate the server template itself - new_template = super().copy(default) - - # Duplicate variable values - for variable_value in self.variable_value_ids: - variable_value.with_context(reference_mixin_skip_self=True).copy( - {"server_template_id": new_template.id} - ) - - # Duplicate server logs - for server_log in self.server_log_ids: - server_log.copy({"server_template_id": new_template.id}) - - return new_template - - def action_create_server(self): - """ - Returns wizard action to create new server - """ - self.ensure_one() - context = self.env.context.copy() - context.update( - { - "default_server_template_id": self.id, # pylint: disable=no-member - "default_color": self.color, - "default_ssh_port": self.ssh_port, - "default_ssh_username": self.ssh_username, - "default_ssh_password": self.ssh_password, - "default_ssh_key_id": self.ssh_key_id.id, - "default_ssh_auth_mode": self.ssh_auth_mode, - "default_plan_delete_id": self.plan_delete_id.id, - } - ) - if self.variable_value_ids: - context.update( - { - "default_line_ids": [ - ( - 0, - 0, - { - "variable_value_id": line.id, - }, - ) - for line in self.variable_value_ids - ] - } - ) - return { - "type": "ir.actions.act_window", - "name": _("Create Server"), - "res_model": "cx.tower.server.template.create.wizard", - "view_mode": "form", - "target": "new", - "context": context, - } - - def action_open_servers(self): - """ - Return action to open related servers - """ - self.ensure_one() - action = self.env["ir.actions.act_window"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_server" - ) - action.update( - { - "domain": [("server_template_id", "=", self.id)], # pylint: disable=no-member - } - ) - return action - - @api.model - def create_server_from_template(self, template_reference, server_name, **kwargs): - """This is a wrapper function that is meant to be called - when we need to create a server from specific server template - - Args: - template_reference (Char): Server template reference - server_name (Char): Name of the new server - - Kwargs: - partner (res.partner(), optional): Partner this server belongs to. - ipv4 (Char, optional): IP v4 address. Defaults to None. - ipv6 (Char, optional): IP v6 address. - Must be provided in case IP v4 is not. Defaults to None. - ssh_password (Char, optional): SSH password. Defaults to None. - ssh_key (Char, optional): SSH private key record reference. - Defaults to None. - configuration_variables (Dict, optional): Custom configuration variable. - Following format is used: - `variable_reference`: `variable_value_char` - eg: - {'branch': 'prod', 'odoo_version': '16.0'} - pick_all_template_variables (bool): This parameter ensures that the server - being created considers existing variables from the template. - If enabled, the template variables will also be included in the server - variables. The default value is True. - - Returns: - cx.tower.server: newly created server record - """ - template = self.get_by_reference(template_reference) - return template._create_new_server(server_name, **kwargs) - - def _create_new_server(self, name, **kwargs): - """Creates a new server from template - - Args: - name (Char): Name of the new server - - Kwargs: - partner (res.partner(), optional): Partner this server belongs to. - ipv4 (Char, optional): IP v4 address. Defaults to None. - ipv6 (Char, optional): IP v6 address. - Must be provided in case IP v4 is not. Defaults to None. - ssh_password (Char, optional): SSH password. Defaults to None. - ssh_key (Char, optional): SSH private key record reference. - Defaults to None. - configuration_variables (Dict, optional): Custom configuration variable. - Following format is used: - `variable_reference`: `variable_value_char` - eg: - {'branch': 'prod', 'odoo_version': '16.0'} - pick_all_template_variables (bool): This parameter ensures that the server - being created considers existing variables from the template. - If enabled, the template variables will also be included in the server - variables. The default value is True. - - Returns: - cx.tower.server: newly created server record - """ - self.ensure_one() - - # Retrieve the passed variables - configuration_variables = kwargs.get("configuration_variables", {}) - - # We validate mandatory variables - if not kwargs.get("pick_all_template_variables"): - self._validate_required_variables(configuration_variables) - - # We are using sudo to ensure all values are copied - server_values = self.sudo()._prepare_server_values( - name=name, - server_template_id=self.id, # pylint: disable=no-member - **kwargs, - ) - - # Pop variable values to add them after server creation. - # This is needed to ensure that access rules are applied properly. - variable_values = server_values.pop("variable_value_ids") - - # Prepare context for server creation - context = self.env.context.copy() - - # SSH setting may be added after server creation. - context.update({"skip_ssh_settings_check": True}) - # We need to remove default_server_template_id to avoid it being used - # in variable values. - context.pop("default_server_template_id", None) - - # Create server - server = ( - self.env["cx.tower.server"] # pylint: disable=context-overridden # new need a new clean context - .sudo() - .with_context(context) - .create(server_values) - .sudo() - ) - - # Add variable values - if variable_values: - server.with_context(context).write({"variable_value_ids": variable_values}) # pylint: disable=context-overridden # new need a new clean context - - # Create server logs - logs = server.server_log_ids.filtered(lambda rec: rec.log_type == "file") - for log in logs.sudo(): - log.file_id = log.file_template_id.create_file( - server=server, if_file_exists="skip" - ).id - - flight_plan = server.server_template_id.flight_plan_id - if flight_plan: - server.run_flight_plan(flight_plan) - - return server - - def _get_post_create_fields(self): - """ - Add fields that should be populated after server template creation - """ - res = super()._get_post_create_fields() - return res + ["variable_value_ids", "server_log_ids"] - - def _get_fields_tower_server(self): - """ - Return field name list to read from template and create new server - """ - return [ - "ssh_username", - "ssh_password", - "ssh_key_id", - "ssh_auth_mode", - "use_sudo", - "color", - "os_id", - "plan_delete_id", - "tag_ids", - "variable_value_ids", - "server_log_ids", - "shortcut_ids", - "scheduled_task_ids", - ] - - def _prepare_server_values(self, pick_all_template_variables=True, **kwargs): - """ - Prepare the server values to create a new server based on - the current template. It reads all fields from the template, copies them, - and processes One2many fields to create new related records. Magic fields - like 'id', concurrency fields, and audit fields are excluded from the copied - data. - - Args: - pick_all_template_variables (bool): This parameter ensures that the server - being created considers existing variables from the template. - If enabled, the template variables will also be included in the server - variables. The default value is True. - **kwargs: Additional values to update in the final server record. - - Returns: - list: A list of dictionaries representing the values for the new server - records. - """ - model_fields = self._fields - field_o2m_type = fields.One2many - - # define the magic fields that should not be copied - # (including ID and concurrency fields) - MAGIC_FIELDS = models.MAGIC_COLUMNS + [self.CONCURRENCY_CHECK_FIELD] - - # read all values required to create a new server from the template - values = self.read(self._get_fields_tower_server(), load=False)[0] - - # prepare server config values from kwargs - server_config_values = self._parse_server_config_values(kwargs) - template = self.browse(values["id"]) - - # Process each field in the template - for field in values.keys(): - if isinstance(model_fields[field], field_o2m_type): - # get related records for One2many field - related_records = getattr(template, field) - new_records = [] - # for each related record, read its data and prepare it for copying - for record in related_records: - record_data = { - k: v - for k, v in record.read(load=False)[0].items() - if k not in MAGIC_FIELDS - } - # set the inverse field (link back to the template) - # to False to unlink from the original template - record_data[model_fields[field].inverse_name] = False - new_records.append((0, 0, record_data)) - - values[field] = new_records - - # Handle configuration variables if provided. - configuration_variables = kwargs.pop("configuration_variables", None) - configuration_variable_options = kwargs.pop( - "configuration_variable_options", {} - ) - - if configuration_variables: - # Validate required variables - self._validate_required_variables(configuration_variables) - - # Search for existing variable options. - option_references = list(configuration_variable_options.values()) - existing_options = option_references and self.env[ - "cx.tower.variable.option" - ].search([("reference", "in", option_references)]) - missing_options = list( - set(option_references) - - {option.reference for option in existing_options} - ) - - if missing_options: - # Map variable references to their corresponding - # invalid option references. - missing_options_to_variables = { - var_ref: opt_ref - for var_ref, opt_ref in configuration_variable_options.items() - if opt_ref in missing_options - } - # Generate a detailed error message for invalid variable options. - detailed_message = "\n".join( - _( - "Variable reference '%(var_ref)s' has an invalid " - "option reference '%(opt_ref)s'.", - var_ref=var_ref, - opt_ref=opt_ref, - ) - for var_ref, opt_ref in missing_options_to_variables.items() - ) - raise ValidationError( - _( - "Some variable options are invalid:\n%(detailed_message)s", - detailed_message=detailed_message, - ) - ) - - # Map variable options to their IDs. - configuration_variable_options_dict = { - option.variable_id.id: option for option in existing_options - } - - variable_obj = self.env["cx.tower.variable"] - variable_references = list(configuration_variables.keys()) - - # Search for existing variables or create new ones if missing. - exist_variables = variable_obj.search( - [("reference", "in", variable_references)] - ) - missing_references = list( - set(variable_references) - - {variable.reference for variable in exist_variables} - ) - variable_vals_list = [ - {"name": reference} for reference in missing_references - ] - new_variables = variable_obj.create(variable_vals_list) - all_variables = exist_variables | new_variables - - # Build a dictionary {variable: variable_value}. - configuration_variable_dict = { - variable: configuration_variables[variable.reference] - for variable in all_variables - } - - server_variable_vals_list = [] - for variable, variable_value in configuration_variable_dict.items(): - variable_option = configuration_variable_options_dict.get(variable.id) - - server_variable_vals_list.append( - ( - 0, - 0, - { - "variable_id": variable.id, - "value_char": variable_option - and variable_option.value_char - or variable_value, - "option_id": variable_option and variable_option.id, - }, - ) - ) - - if pick_all_template_variables: - # update or add variable values - existing_variable_values = values.get("variable_value_ids", []) - variable_id_to_index = { - cmd[2]["variable_id"]: idx - for idx, cmd in enumerate(existing_variable_values) - if cmd[0] == 0 and "variable_id" in cmd[2] - } - - # Update exist variable options - for exist_variable_id, index in variable_id_to_index.items(): - option = configuration_variable_options_dict.get(exist_variable_id) - if not option: - continue - existing_variable_values[index][2].update( - { - "option_id": option.id, - "value_char": option.value_char, - } - ) - - # Prepare new command values for server variables - for new_command in server_variable_vals_list: - variable_id = new_command[2]["variable_id"] - if variable_id in variable_id_to_index: - idx = variable_id_to_index[variable_id] - # update exist command - existing_variable_values[idx] = new_command - else: - # add new command - existing_variable_values.append(new_command) - - values["variable_value_ids"] = existing_variable_values - else: - values["variable_value_ids"] = server_variable_vals_list - - # remove the `id` field to ensure a new record is created - # instead of updating the existing one - del values["id"] - # update the values with additional arguments from kwargs - values.update(kwargs) - # update server configs - values.update(server_config_values) - # Add current user as user/manager to the newly created server - values.update( - { - "user_ids": [(6, 0, self._default_user_ids())], - "manager_ids": [(6, 0, self._default_manager_ids())], - } - ) - - return values - - def _parse_server_config_values(self, config_values): - """ - Prepares server configuration values. - - Args: - config_values (dict): A dictionary containing server configuration values. - Keys and their expected values: - - partner (res.partner, optional): The partner this server - belongs to. - - ipv4 (str, optional): IPv4 address. Defaults to None. - - ipv6 (str, optional): IPv6 address. Must be provided if IPv4 is - not specified. Defaults to None. - - ssh_key (str, optional): Reference to an SSH private key record. - Defaults to None. - - Returns: - dict: A dictionary containing parsed server configuration values with the - following keys: - - partner_id (int, optional): ID of the partner. - - ssh_key_id (int, optional): ID of the associated SSH key. - - ip_v4_address (str, optional): Parsed IPv4 address. - - ip_v6_address (str, optional): Parsed IPv6 address. - """ - values = {} - - # This field is always populated from Server Template and - # cannot be altered with function params. - config_values.pop("plan_delete_id", None) - - partner = config_values.pop("partner", None) - if partner: - values["partner_id"] = partner.id - - ssh_key_reference = config_values.pop("ssh_key", None) - if ssh_key_reference: - ssh_key = self.env["cx.tower.key"].get_by_reference(ssh_key_reference) - if ssh_key: - values["ssh_key_id"] = ssh_key.id - - ipv4 = config_values.pop("ipv4", None) - if ipv4: - values["ip_v4_address"] = ipv4 - - ipv6 = config_values.pop("ipv6", None) - if ipv6: - values["ip_v6_address"] = ipv6 - - return values - - def _validate_required_variables(self, configuration_variables): - """ - Validate that all required variables are present, not empty, - and that no required variable is entirely missing from the configuration. - - Args: - configuration_variables (dict): A dictionary of variable references - and their values. - - Raises: - ValidationError: If all required variables are - missing from the configuration, - or if any required variable is empty or missing. - """ - required_variables = self.variable_value_ids.filtered("required") - if not required_variables: - return - - required_refs = [var.variable_reference for var in required_variables] - config_refs = list(configuration_variables.keys()) - - missing_variables = [ref for ref in required_refs if ref not in config_refs] - empty_variables = [ - ref - for ref in required_refs - if ref in config_refs and not configuration_variables[ref] - ] - - if not (missing_variables or empty_variables): - return - - error_parts = [ - _("Please resolve the following issues with configuration variables:") - ] - - if missing_variables: - error_parts.append( - _( - " - Missing variables: %(variables)s", - variables=", ".join(missing_variables), - ) - ) - - if empty_variables: - error_parts.append( - _( - " - Empty values for variables: %(variables)s", - variables=", ".join(empty_variables), - ) - ) - - raise ValidationError("\n".join(error_parts)) - - def _get_dependent_model_relation_fields(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_dependent_model_relation_fields() - return res + ["variable_value_ids"] diff --git a/addons/cetmix_tower_server/models/cx_tower_shortcut.py b/addons/cetmix_tower_server/models/cx_tower_shortcut.py deleted file mode 100644 index f52c955..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_shortcut.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (C) 2024 Cetmix OÜ -# License OPL-1 (https://apps.odoocdn.com/loempia/static/examples/LICENSE). -from odoo import _, fields, models - - -class CxTowerShortcut(models.Model): - """ - Cetmix Tower Shortcut. - Used to run commands or flight plans with a single click. - """ - - _name = "cx.tower.shortcut" - _inherit = ["cx.tower.access.mixin", "cx.tower.reference.mixin"] - _description = "Cetmix Tower Shortcut" - _order = "sequence, name" - - active = fields.Boolean(default=True) - sequence = fields.Integer(default=10) - server_ids = fields.Many2many( - string="Servers", - comodel_name="cx.tower.server", - relation="cx_tower_server_shortcut_rel", - column1="shortcut_id", - column2="server_id", - ) - server_template_ids = fields.Many2many( - string="Server Templates", - comodel_name="cx.tower.server.template", - relation="cx_tower_server_template_shortcut_rel", - column1="shortcut_id", - column2="server_template_id", - ) - action = fields.Selection( - selection=[("command", "Command"), ("plan", "Flight Plan")], required=True - ) - command_id = fields.Many2one(comodel_name="cx.tower.command") - use_sudo = fields.Boolean( - help="Run command using 'sudo'", - ) - plan_id = fields.Many2one(string="Flight Plan", comodel_name="cx.tower.plan") - note = fields.Text() - - def run(self, server=None): - """Runs related shortcut action - - Args: - server (cx.tower.server): Server to run the shortcut. - """ - self.ensure_one() - - # Try to obtain server from context if not provided as an argument - if server is None: - server_id = self.env.context.get("server_id") - - # Just return, no exceptions for now - if not server_id: - return - - server = self.env["cx.tower.server"].browse(server_id) - - # Just return, no exceptions for now - if not server: - return - - # Use the first server record if several are passed - if len(server) > 1: - server = server[0] - if self.action == "command" and self.command_id: - server.run_command(self.sudo().command_id, sudo=self.use_sudo) - elif self.action == "plan" and self.plan_id: - server.run_flight_plan(self.sudo().plan_id) - - # Notify - return self._notify_on_run(server) - - def _notify_on_run(self, server): - """Send notification when plan is triggered. - Override to implement custom notifications. - - Args: - server (cx.tower.server()): Server action was triggered for - - Returns: - `ir.action.client`: Web client notification. - """ - self.ensure_one() - - notification = { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "title": _("%(shr)s triggered", shr=self.name), - "message": _( - "Check %(t)s log for result", - t="flight plan" if self.action == "plan" else "command", - ), - "sticky": False, - }, - } - return notification diff --git a/addons/cetmix_tower_server/models/cx_tower_tag.py b/addons/cetmix_tower_server/models/cx_tower_tag.py deleted file mode 100644 index f7fd206..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_tag.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, fields, models -from odoo.exceptions import ValidationError - - -class CxTowerTag(models.Model): - """ - Cetmix Tower Tag. - Tags are used to group servers, commands, flight plans, etc. - """ - - _name = "cx.tower.tag" - _inherit = [ - "cx.tower.reference.mixin", - ] - _description = "Cetmix Tower Tag" - _order = "name" - - color = fields.Integer(help="For better visualization in views") - - # --- Relations - server_ids = fields.Many2many( - comodel_name="cx.tower.server", - relation="cx_tower_server_tag_rel", - column1="tag_id", - column2="server_id", - string="Servers", - ) - command_ids = fields.Many2many( - comodel_name="cx.tower.command", - relation="cx_tower_command_tag_rel", - column1="tag_id", - column2="command_id", - string="Commands", - ) - plan_ids = fields.Many2many( - comodel_name="cx.tower.plan", - relation="cx_tower_plan_tag_rel", - column1="tag_id", - column2="plan_id", - string="Plans", - ) - server_template_ids = fields.Many2many( - comodel_name="cx.tower.server.template", - relation="cx_tower_server_template_tag_rel", - column1="tag_id", - column2="server_template_id", - string="Server Templates", - ) - file_template_ids = fields.Many2many( - comodel_name="cx.tower.file.template", - relation="cx_tower_file_template_tag_rel", - column1="tag_id", - column2="file_template_id", - string="File Templates", - ) - - def unlink(self): - """ - Prevent deletion of tags that are in use - unless user is root or using sudo. - """ - if not self.env.is_superuser() and not self.env.user.has_group( - "cetmix_tower_server.group_root" - ): - self._check_tags_can_be_deleted() - return super().unlink() - - def _check_tags_can_be_deleted(self): - """Check if tags can be deleted. - - Raises: - ValidationError: If tag is in use - """ - - for tag in self: - if ( - tag.server_ids - or tag.command_ids - or tag.plan_ids - or tag.server_template_ids - or tag.file_template_ids - ): - raise ValidationError( - _( - "Cannot delete tag '%(tag_name)s' because" - " it is used in related records.", - tag_name=tag.name, - ) - ) diff --git a/addons/cetmix_tower_server/models/cx_tower_tag_mixin.py b/addons/cetmix_tower_server/models/cx_tower_tag_mixin.py deleted file mode 100644 index b36a929..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_tag_mixin.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) 2025 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class CxTowerTagMixin(models.AbstractModel): - """ - Cetmix Tower Tag Mixin. - Used to add tag functionality to models. - """ - - _name = "cx.tower.tag.mixin" - _description = "Cetmix Tower Tag Mixin" - - tag_ids = fields.Many2many( - comodel_name="cx.tower.tag", - string="Tags", - ) - - def add_tags(self, tag_names): - """Add tags to the record - - Args: - tag_names (list of Char or Char): List of tag names to add - or single tag name - """ - # Single tag name is given, convert to list - if isinstance(tag_names, str): - tag_names = [tag_names] - # Invalid type is given, return True - elif not isinstance(tag_names, list): - return True - - tags = self.env["cx.tower.tag"].search([("name", "in", tag_names)]) - if tags: - self.write({"tag_ids": [(4, tag.id) for tag in tags]}) - return True - - def remove_tags(self, tag_names): - """Remove tags from the record - - Args: - tag_names (list of Char or Char): List of tag names to remove - or single tag name. - """ - # Single tag name is given, convert to list - if isinstance(tag_names, str): - tag_names = [tag_names] - # Invalid type is given, return True - elif not isinstance(tag_names, list): - return True - - tags = self.env["cx.tower.tag"].search([("name", "in", tag_names)]) - if tags: - self.write({"tag_ids": [(3, tag.id) for tag in tags]}) - return True - - def has_tags(self, tag_name, search_all=False): - """Get all records from the recordset that have any of the given tags - - Args: - tag_name (Char or List of Char): Tag name or list of tag names to check - search_all (bool): If True, search all records in the model - """ - - # Empty recordset is returned as is - if not self and not search_all: - return self - - # Check argument type - if isinstance(tag_name, str): - single_tag = True - elif isinstance(tag_name, list): - single_tag = False - else: - return self.browse() - - if search_all: - if single_tag: - domain = [("tag_ids.name", "=", tag_name)] - else: - domain = [("tag_ids.name", "in", tag_name)] - return self.env[self._name].search(domain) - - if single_tag: - return self.filtered( - lambda record: tag_name in record.tag_ids.mapped("name") - ) - return self.filtered( - lambda record: set(tag_name) & set(record.tag_ids.mapped("name")) - ) - - def has_all_tags(self, tag_names, search_all=False): - """Get all records from the recordset that have all of the given tags - - Args: - tag_names (list of Char): List of tag names to check - search_all (bool): If True, search all records in the model - """ - # No value or invalid type is given, return empty recordset - if not tag_names or not isinstance(tag_names, list): - return self.browse() - - # Empty recordset is returned as is - if not self and not search_all: - return self - - if search_all: - records = self.env[self._name].search([("tag_ids.name", "in", tag_names)]) - else: - records = self - - tag_names_set = set(tag_names) - return records.filtered( - lambda record: tag_names_set.issubset(record.tag_ids.mapped("name")) - ) diff --git a/addons/cetmix_tower_server/models/cx_tower_template_mixin.py b/addons/cetmix_tower_server/models/cx_tower_template_mixin.py deleted file mode 100644 index d1c2431..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_template_mixin.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright (C) 2024 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from jinja2 import Environment, Template, meta -from jinja2.exceptions import TemplateSyntaxError - -from odoo import _, api, fields, models -from odoo.exceptions import UserError, ValidationError - - -class CxTowerTemplateMixin(models.AbstractModel): - """Used to implement template rendering functions. - Inherit in your model in case you want to render variable values in it. - """ - - _name = "cx.tower.template.mixin" - _description = "Cetmix Tower template rendering mixin" - - code = fields.Text(help="This field will be rendered using variables") - variable_ids = fields.Many2many( - string="Variables", - comodel_name="cx.tower.variable", - compute="_compute_variable_ids", - store=True, - copy=False, - ) - - @classmethod - def _get_depends_fields(cls): - """ - Define dependent fields for the `variable_ids` computation. - - This method should be overridden in inheriting models to provide - a list of fields that influence the computation of `variable_ids`. - These fields are used in the `@api.depends` decorator to trigger - recomputation when their values change. - - Returns: - list: A list of field names (str) that are dependencies for - the `variable_ids` computation. Default is an empty list. - - Example: - In a subclass, override as follows: - >>> @classmethod - >>> def _get_depends_fields(cls): - >>> return ["code", "path"] - """ - return [] - - @api.depends(lambda self: self._get_depends_fields()) - def _compute_variable_ids(self): - """ - Compute the values of the `variable_ids` - field based on model-specific dependencies. - - This method retrieves the dependent fields using `_get_depends_fields` - and dynamically calculates the values of `variable_ids` using the - `_prepare_variable_commands` method. - - If no dependent fields or relation parameters are defined, the field - is reset to an empty list. - - Example: - If dependent fields include `code` and `path`, and the model-specific - logic links them to variables, this method will update the `variable_ids` - field accordingly. - - Raises: - ValidationError: If the field metadata is incorrectly defined or - missing required attributes. - - Returns: - None: The field `variable_ids` is updated in-place for each record. - """ - depends_fields = self._get_depends_fields() - - for record in self: - if depends_fields: - record.variable_ids = record._prepare_variable_commands(depends_fields) - else: - record.variable_ids = [(5, 0, 0)] - - def render_code(self, pythonic_mode=False, **kwargs): - """Render record 'code' field using variables from kwargs - Call to render recordset of the inheriting models - Args: - pythonic_mode (Bool): If True, all variables in kwargs are converted to - strings and wrapped in double quotes. - Default is False. - **kwargs (dict): {variable: value, ...} - Returns: - dict {record_id: rendered_code, ...} - """ - return { - rec.id: self.render_code_custom(rec.code, pythonic_mode, **kwargs) - for rec in self - } - - def render_code_custom(self, code, pythonic_mode=False, **kwargs): - """ - Render custom code using variables from kwargs - - This method renders a template string (code) using the variables provided - in kwargs. If pythonic_mode is enabled, all variables are automatically - converted to strings and enclosed in double quotes before rendering. - - Args: - code (Text): code to render (eg 'some {{ custom }} text') - pythonic_mode (Bool): If True, all variables in kwargs are converted to - strings and wrapped in double quotes. - Default is False. - **kwargs (dict): {variable: value, ...} - Returns: - rendered_code (text): The resulting string after rendering the template with - the provided variables. - """ - try: - if pythonic_mode: - kwargs = { - key: self._make_value_pythonic(value) - for key, value in kwargs.items() - } - return Template(code, trim_blocks=True).render(kwargs) - except Exception as e: - raise UserError(str(e)) from e - - def get_variables(self): - """Get the list of variables for templates - Call to get variables for recordset of the inheriting models - - Returns: - dict {'record_id': {variables}...} - NB: 'record_id' is String - """ - res = {} - for rec in self: - res[str(rec.id)] = self.get_variables_from_code(rec.code) - return res - - def get_variables_from_code(self, code): - """Get the list of variables for templates - Call to get variables from custom code string - - Args: - code (Text) custom code (eg 'Custom {{ var }} {{ var2 }} ...') - Returns: - variables (List) variables (eg ['var','var2',..]) - """ - env = Environment() - try: - ast = env.parse(code) - undeclared_variables = meta.find_undeclared_variables(ast) - return list(undeclared_variables) if undeclared_variables else [] - except TemplateSyntaxError as e: - raise ValidationError(_("Variable syntax error: %s", e)) from e - - def _prepare_variable_commands(self, field_names, force_record=None): - """ - Prepares commands to set variable references from the given fields. - - Args: - field_names (list): List of field names to extract variable references from. - force_record (record, optional): A record to use instead of the current one. - - Returns: - list: An Odoo command to assign or clear variable references. - """ - record = force_record or self - record.ensure_one() - - all_references = set() - for field_name in field_names: - value = getattr(record, field_name, None) - if value: - all_references.update(self.get_variables_from_code(value)) - - if all_references: - variables = self.env["cx.tower.variable"].search( - [("reference", "in", list(all_references))] - ) - command = [(6, 0, variables.ids)] - else: - command = [(5, 0, 0)] - - return command - - def _make_value_pythonic(self, value): - """Prepares value for use in 'pythonic' mode - by enclosing strings into double quotes - - Args: - value (Char): value to process - - Returns: - Char: processed value - """ - - # Nothing to do here - if isinstance(value, bool) or value is None: - result = value - - # Handle nested dicts such as system variables - elif isinstance(value, dict): - result = {} - for key, val in value.items(): - result.update({key: self._make_value_pythonic(val)}) - else: - # Enclose in double quotes - result = f'"{value}"' - return result diff --git a/addons/cetmix_tower_server/models/cx_tower_variable.py b/addons/cetmix_tower_server/models/cx_tower_variable.py deleted file mode 100644 index 351769f..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_variable.py +++ /dev/null @@ -1,368 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging - -from odoo import _, api, fields, models -from odoo.tools.safe_eval import wrap_module - -_logger = logging.getLogger(__name__) - -re = wrap_module( - __import__("re"), - [ - "match", - "fullmatch", - "search", - "sub", - "subn", - "split", - "findall", - "finditer", - "compile", - "template", - "escape", - "error", - ], -) - - -class TowerVariable(models.Model): - """Variables""" - - _name = "cx.tower.variable" - _description = "Cetmix Tower Variable" - _inherit = [ - "cx.tower.reference.mixin", - "cx.tower.access.mixin", - "cx.tower.tag.mixin", - ] - - _order = "name" - - DEFAULT_VALIDATION_MESSAGE = _("Invalid value!") - - value_ids = fields.One2many( - string="Values", - comodel_name="cx.tower.variable.value", - inverse_name="variable_id", - ) - value_ids_count = fields.Integer( - string="Value Count", compute="_compute_variable_counters" - ) - option_ids = fields.One2many( - comodel_name="cx.tower.variable.option", - inverse_name="variable_id", - string="Options", - auto_join=True, - ) - variable_type = fields.Selection( - selection=[("s", "String"), ("o", "Options")], - default="s", - required=True, - string="Type", - ) - applied_expression = fields.Text( - help="Python expression to apply to the variable value. \n" - "You can use general python sting functions and 're' module " - "for regex operations. " - "Use 'value' variable to refer to the variable value, use 'result'" - " to assign the final result that will be used as a variable value.\n" - "Eg 'result = value.lower().replace(' ', '_')'", - ) - validation_pattern = fields.Char( - help="Regex pattern to validate the variable values using the " - "'re.match' function. Eg. ^[a-z0-9]+$ \n" - "If empty, the variable values will not be validated.", - ) - validation_message = fields.Char( - translate=True, - help="Message to display when the variable value is invalid. \n" - "First line will be added automatically: " - "`Variable:, Value: `\n" - "Eg: `Variable: Customer Name, Value: Test\nInvalid value!`\n" - "If empty, the default message will be used.", - ) - note = fields.Text( - help="Additional notes about the variable. \n" - "This field will be displayed in the variable form.", - ) - - # --- Link to records where the variable is used - command_ids = fields.Many2many( - comodel_name="cx.tower.command", - relation="cx_tower_command_variable_rel", - column1="variable_id", - column2="command_id", - copy=False, - ) - command_ids_count = fields.Integer( - string="Command Count", compute="_compute_variable_counters" - ) - plan_line_ids = fields.Many2many( - comodel_name="cx.tower.plan.line", - relation="cx_tower_plan_line_variable_rel", - column1="variable_id", - column2="plan_line_id", - copy=False, - ) - plan_line_ids_count = fields.Integer( - string="Plan Line Count", compute="_compute_variable_counters" - ) - file_ids = fields.Many2many( - comodel_name="cx.tower.file", - relation="cx_tower_file_variable_rel", - column1="variable_id", - column2="file_id", - copy=False, - ) - file_ids_count = fields.Integer( - string="File Count", compute="_compute_variable_counters" - ) - file_template_ids = fields.Many2many( - comodel_name="cx.tower.file.template", - relation="cx_tower_file_template_variable_rel", - column1="variable_id", - column2="file_template_id", - copy=False, - ) - file_template_ids_count = fields.Integer( - string="File Template Count", compute="_compute_variable_counters" - ) - variable_value_ids = fields.Many2many( - comodel_name="cx.tower.variable.value", - relation="cx_tower_variable_value_variable_rel", - column1="variable_id", - column2="variable_value_id", - copy=False, - ) - variable_value_ids_count = fields.Integer( - string="Variable Value Count", compute="_compute_variable_counters" - ) - - _sql_constraints = [("name_uniq", "unique (name)", "Variable names must be unique")] - - def _compute_variable_counters(self): - """Count number of variable values for the variable""" - for rec in self: - rec.update( - { - "variable_value_ids_count": len(rec.variable_value_ids), - "command_ids_count": len(rec.command_ids), - "plan_line_ids_count": len(rec.plan_line_ids), - "file_ids_count": len(rec.file_ids), - "file_template_ids_count": len(rec.file_template_ids), - "value_ids_count": len(rec.value_ids), - } - ) - - def action_open_values(self): - self.ensure_one() - context = self.env.context.copy() - context.update( - { - "default_variable_id": self.id, - } - ) - - return { - "type": "ir.actions.act_window", - "name": _("Variable Values"), - "res_model": "cx.tower.variable.value", - "views": [[False, "tree"]], - "target": "current", - "context": context, - "domain": [("variable_id", "=", self.id)], - } - - def action_open_commands(self): - """Open the commands where the variable is used""" - - self.ensure_one() - action = self.env["ir.actions.act_window"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_command" - ) - action.update( - { - "domain": [("variable_ids", "in", self.ids)], - } - ) - return action - - def action_open_plan_lines(self): - """Open the plan lines where the variable is used""" - self.ensure_one() - return { - "type": "ir.actions.act_window", - "name": _("Plan Lines"), - "res_model": "cx.tower.plan.line", - "views": [ - [False, "tree"], - [ - self.env.ref("cetmix_tower_server.cx_tower_plan_line_view_form").id, - "form", - ], - ], - "target": "current", - "domain": [("variable_ids", "in", self.ids)], - } - - def action_open_files(self): - """Open the files where the variable is used""" - self.ensure_one() - action = self.env["ir.actions.act_window"]._for_xml_id( - "cetmix_tower_server.cx_tower_file_action" - ) - action.update( - { - "domain": [("variable_ids", "in", self.ids)], - } - ) - return action - - def action_open_file_templates(self): - """Open the file templates where the variable is used""" - self.ensure_one() - action = self.env["ir.actions.act_window"]._for_xml_id( - "cetmix_tower_server.cx_tower_file_template_action" - ) - action.update( - { - "domain": [("variable_ids", "in", self.ids)], - } - ) - return action - - def action_open_variable_values(self): - """Open the variable values where the variable is used""" - self.ensure_one() - return { - "type": "ir.actions.act_window", - "name": _("Variable Values"), - "res_model": "cx.tower.variable.value", - "views": [[False, "tree"]], - "target": "current", - "domain": [("variable_ids", "in", self.ids)], - } - - @api.model - def _get_eval_context(self, value_char=None): - """ - Evaluation context to pass to safe_eval to evaluate - the Python expression used in the `applied_expression` field - - Args: - value_char (Char): variable value - - Returns: - dict: evaluation context - """ - return { - "re": re, - "value": value_char, - } - - # Reference rename propagation - - def write(self, vals): - """Override the write method to propagate variable reference updates. - - Records the old reference values, performs the write, and if the reference - field has changed, initiates propagation to update related records. - """ - old_refs = ( - {rec.id: rec.reference for rec in self} if "reference" in vals else {} - ) - res = super().write(vals) - if "reference" in vals: - for rec in self: - old_ref = old_refs.get(rec.id) - if old_ref and old_ref != rec.reference: - rec._propagate_reference_change(old_ref, rec.reference) - return res - - def _propagate_reference_change(self, old_ref, new_ref): - """Replace all occurrences of an old variable reference with a new one. - - Compiles a pattern matching the old Jinja-style reference, then searches across - configured models and fields to substitute any matches, preserving formatting. - """ - pattern = re.compile(r"(\{\{\s*)" + re.escape(old_ref) + r"(\s*\}\})") - - def _replace(text): - """Helper to replace old_ref with new_ref in the given text.""" - return pattern.sub(lambda m: f"{m.group(1)}{new_ref}{m.group(2)}", text) - - model_fields_map = self._get_propagation_field_mapping() - - for model_name, field_names in model_fields_map.items(): - Model = self.env[model_name] - - if model_name == "cx.tower.variable.value": - domain = [("variable_id", "=", self.id)] - else: - domain = [("variable_ids", "in", self.ids)] - - for record in Model.search(domain): - vals = {} - for field_name in field_names: - value = record[field_name] - if isinstance(value, str) and old_ref in value: - new_value = _replace(value) - if new_value != value: - vals[field_name] = new_value - - if vals: - record.with_context(skip_reference_propagation=True).write(vals) - _logger.debug( - "Variable reference updated in %s(%s): %s", - model_name, - record.id, - ", ".join(vals.keys()), - ) - - def _get_propagation_field_mapping(self): - """Return the mapping of models to fields for reference change propagation. - - The returned dict maps each model name to a list of field names - that may contain variable references requiring updates. - """ - return { - "cx.tower.command": ["code", "path"], - "cx.tower.file": ["code", "server_dir", "name"], - "cx.tower.file.template": ["code", "server_dir", "file_name"], - "cx.tower.variable.value": ["value_char"], - "cx.tower.plan.line": ["condition"], - } - - def _get_dependent_model_relation_fields(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_dependent_model_relation_fields() - return res + ["value_ids"] - - def _validate_value(self, value_char=None): - """ - Validate the variable value - - Args: - value_char (Char): variable value - - Returns: - (Boolean, Char): (is_valid, validation_message) - """ - self.ensure_one() - if ( - not self.validation_pattern - or not value_char - or re.match(self.validation_pattern, value_char) - ): - return True, None - message = self.validation_message or self.DEFAULT_VALIDATION_MESSAGE - return ( - False, - _( - "Variable: %(var)s, Value: %(val)s\n%(msg)s", - msg=message, - var=self.name, - val=value_char, - ), - ) diff --git a/addons/cetmix_tower_server/models/cx_tower_variable_mixin.py b/addons/cetmix_tower_server/models/cx_tower_variable_mixin.py deleted file mode 100644 index ccc05ed..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_variable_mixin.py +++ /dev/null @@ -1,283 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging -import re -import uuid - -from odoo import fields, models -from odoo.tools.safe_eval import safe_eval - -_logger = logging.getLogger(__name__) - - -class TowerVariableMixin(models.AbstractModel): - """Used to implement variables and variable values. - Inherit in your model if you want to use variables in it. - """ - - _name = "cx.tower.variable.mixin" - _description = "Tower Variables mixin" - - variable_value_ids = fields.One2many( - string="Variable Values", - comodel_name="cx.tower.variable.value", - auto_join=True, - help="Variable values for selected record", - ) - - def get_variable_values(self, variable_references, apply_modifiers=True): - """Get variable values for selected records - - Args: - variable_references (list of Char): variable names - apply_modifiers (bool): apply Python modifiers to the values - - Returns: - dict {record_id: {variable_reference: value}} - """ - res = {} - - # Get global values first - if variable_references: - global_values = self.get_global_variable_values(variable_references) - - # Get record wise values - for rec in self: - res_vars = global_values.get( - rec.id, {} - ) # set global values as defaults - for variable_reference in variable_references: - # Check if this is a system variable - system_value = self._get_system_variable_value(variable_reference) - if system_value: - res_vars.update({variable_reference: system_value}) - - # Get regular value - else: - value = rec.variable_value_ids.filtered( - lambda v, - variable_reference=variable_reference: v.variable_reference - == variable_reference - ) - if value: - res_vars.update({variable_reference: value.value_char}) - - res.update({rec.id: res_vars}) - - # Final render - # Render templates in values - for variable_values in res.values(): - self._render_variable_values(variable_values) - - # Apply modifiers - if apply_modifiers: - self._apply_modifiers(res) - return res - - def get_global_variable_values(self, variable_references): - """Get global values for variables. - Such values do not belong to any record. - - This function is used by get_variable_values() - to compute fallback values. - - Args: - variable_references (list of Char): variable names - - Returns: - dict {record_id: {variable_reference: value}} - """ - res = {} - - if variable_references: - values = self.env["cx.tower.variable.value"].search( - self._compose_variable_global_values_domain(variable_references) - ) - for rec in self: - res_vars = {} - for variable_reference in variable_references: - # Get variable value - value = values.filtered( - lambda v, - variable_reference=variable_reference: v.variable_reference - == variable_reference - ) - res_vars.update( - {variable_reference: value.value_char if value else None} - ) - res.update({rec.id: res_vars}) - return res - - def _get_system_variable_value(self, variable_reference): - """Get the value of a system variable. Eg `tower.server.partner_name` - - Args: - variable_reference (Char): variable value - - Returns: - dict(): populates `tower` variable with with values. - { - 'server': {..server vals..}, - 'tools': {..helper tools vals...} - }. - """ - - # This works for a single record only! - self.ensure_one() - - variable_value = {} - if variable_reference == "tower": - variable_value.update( - { - "server": self._parse_system_variable_server(), - "tools": self._parse_system_variable_tools(), - } - ) - - return variable_value - - def _parse_system_variable_server(self): - """Parser system variable of `server` type. - - Returns: - dict(): `server` values of the `tower` variable. - """ - # Get current server - values = {} - server = self._get_current_server() - if server: - values = { - "name": server.name, - "reference": server.reference, - "username": server.ssh_username, - "partner_name": server.partner_id.name if server.partner_id else False, - "ipv4": server.ip_v4_address, - "ipv6": server.ip_v6_address, - "status": server.status, - "os": server.os_id.name if server.os_id else False, - "url": server.url, - } - return values - - def _parse_system_variable_tools(self): - """Parser system variable of `tools` type. - - Returns: - dict(): `server` values of the `tower` variable. - """ - today = fields.Date.to_string(fields.Date.today()) - now = fields.Datetime.to_string(fields.Datetime.now()) - values = { - "uuid": uuid.uuid4(), - "today": today, - "now": now, - "today_underscore": re.sub(r"[-: .\/]", "_", today), - "now_underscore": re.sub(r"[-: .\/]", "_", now), - } - return values - - def _compose_variable_global_values_domain(self, variable_references): - """Compose domain for global variables - Args: - variable_references (list of Char): variable names - - Returns: - domain - """ - domain = [ - ("is_global", "=", True), - ("variable_reference", "in", variable_references), - ] - return domain - - def _render_variable_values(self, variable_values): - """Renders variable values using other variable values. - For example we have the following values: - "server_root": "/opt/server" - "server_assets": "{{ server_root }}/assets" - - This function will render the "server_assets" variable: - "server_assets": "/opt/server/assets" - - Args: - variable_values (dict): values to complete - """ - self.ensure_one() - TemplateMixin = self.env["cx.tower.template.mixin"] - for key, var_value in variable_values.items(): - # Render only if template is found - if var_value and "{{ " in var_value: - # Get variables used in value - value_vars = TemplateMixin.get_variables_from_code(var_value) - - # Render variables used in value - res = self.get_variable_values(value_vars, apply_modifiers=True) - - # Render value using variables - variable_values[key] = TemplateMixin.render_code_custom( - var_value, **res[self.id] - ) - - def _apply_modifiers(self, variable_values): - """Apply pre-defined Python expression to the dictionary - of variable values. - - Args: - variable_values (dict): variable values - {record_id: {variable_reference: value}} - """ - variable_obj = self.env["cx.tower.variable"] - - for record_id, values in variable_values.items(): - for variable_reference, value in values.items(): - if not value: - continue - - # ORM should cache resolved variables - variable = variable_obj.get_by_reference(variable_reference) - - # Should never happen.. anyway - if not variable: - continue - - # Skip if no expression to apply - if not variable.applied_expression: - continue - - # Evaluate expression - eval_context = variable_obj._get_eval_context(value) - try: - safe_eval( - variable.applied_expression, - eval_context, - mode="exec", - nocopy=True, - ) - variable_values[record_id][variable_reference] = eval_context.get( - "result" - ) - except Exception as e: - _logger.error( - "Error evaluating applied expression for " - "variable %s value %s: %s", - variable.name, - value, - str(e), - ) - - def _get_current_server(self): - """Get current server record. - This is needed to render system variables properly. - - Returns: - cx.tower.server(): server record - """ - self.ensure_one() - - if self._name == "cx.tower.server": - server = self - elif self._name == "cx.tower.variable.value" and self.server_id: - server = self.server_id - else: - server = None - return server diff --git a/addons/cetmix_tower_server/models/cx_tower_variable_option.py b/addons/cetmix_tower_server/models/cx_tower_variable_option.py deleted file mode 100644 index 975b75f..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_variable_option.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError - - -class TowerVariableOption(models.Model): - """ - Model to manage variable options in the Cetmix Tower. - - The model allows defining options - that are linked to tower variables and can be used to - manage configurations or settings for those variables. - """ - - _name = "cx.tower.variable.option" - _description = "Cetmix Tower Variable Options" - _inherit = ["cx.tower.reference.mixin", "cx.tower.access.mixin"] - _order = "sequence, name" - - access_level = fields.Selection( - compute="_compute_access_level", - readonly=False, - store=True, - default=None, - ) - name = fields.Char(required=True) - value_char = fields.Char(string="Value", required=True) - variable_id = fields.Many2one( - comodel_name="cx.tower.variable", - required=True, - ondelete="cascade", - ) - sequence = fields.Integer(default=10) - - # Define a SQL constraint to ensure the combination of - # 'name' and 'variable_id' is unique - _sql_constraints = [ - ( - "unique_variable_option", - "unique (value_char, variable_id)", - "The combination of Value and Variable must be unique.", - ), - ( - "unique_variable_option_name", - "unique (name, variable_id)", - "The combination of Name and Variable must be unique.", - ), - ] - - @api.depends("variable_id", "variable_id.access_level") - def _compute_access_level(self): - """ - Automatically set the access_level based on Variable access level - """ - for rec in self: - if rec.variable_id: - rec.access_level = rec.variable_id.access_level - - @api.constrains("access_level", "variable_id") - def _check_access_level_consistency(self): - """ - Ensure that the access level of the variable value is not lower than - the access level of the associated variable. - """ - access_level_dict = dict( - self.fields_get(["access_level"])["access_level"]["selection"] - ) - for rec in self: - if not rec.variable_id: - continue - if not rec.access_level: - raise ValidationError( - _( - "Access level is not defined for '%(option)s'", - option=rec.name, - ) - ) - if rec.access_level < rec.variable_id.access_level: - raise ValidationError( - _( - "The access level for Variable Option '%(value)s' " - "cannot be lower than the access level of its " - "Variable '%(variable)s'.\n" - "Variable Access Level: %(var_level)s\n" - "Variable Option Access Level: %(val_level)s", - value=rec.name, - variable=rec.variable_id.name, - var_level=access_level_dict[rec.variable_id.access_level], - val_level=access_level_dict[rec.access_level], - ) - ) - - # Workaround for the default value not being set - @api.model_create_multi - def create(self, vals_list): - variable_obj = self.env["cx.tower.variable"] - for vals in vals_list: - # Set access level from the variable - # if not provided explicitly - access_level = vals.get("access_level") - if access_level: - continue - variable_id = vals.get("variable_id") - if variable_id: - variable = variable_obj.browse(variable_id) - vals["access_level"] = variable.access_level - return super().create(vals_list) - - def _get_pre_populated_model_data(self): - """ - Define the model relationships for reference generation. - """ - res = super()._get_pre_populated_model_data() - res.update({"cx.tower.variable.option": ["cx.tower.variable", "variable_id"]}) - return res diff --git a/addons/cetmix_tower_server/models/cx_tower_variable_value.py b/addons/cetmix_tower_server/models/cx_tower_variable_value.py deleted file mode 100644 index 7747d9d..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_variable_value.py +++ /dev/null @@ -1,541 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import re - -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError -from odoo.osv.expression import OR - - -class TowerVariableValue(models.Model): - """ - This model is used to store variable values. - """ - - _name = "cx.tower.variable.value" - _description = "Cetmix Tower Variable Values" - _inherit = [ - "cx.tower.reference.mixin", - "cx.tower.access.mixin", - ] - _rec_name = "variable_reference" - _order = "sequence, variable_reference" - - sequence = fields.Integer(default=10) - access_level = fields.Selection( - compute="_compute_access_level", - readonly=False, - store=True, - default=None, - ) - variable_id = fields.Many2one( - string="Variable", - comodel_name="cx.tower.variable", - required=True, - ondelete="cascade", - ) - name = fields.Char(related="variable_id.name", readonly=True) - variable_reference = fields.Char( - string="Variable Reference", - related="variable_id.reference", - store=True, - index=True, - ) - is_global = fields.Boolean( - string="Global", - compute="_compute_is_global", - inverse="_inverse_is_global", - store=True, - ) - note = fields.Text(related="variable_id.note", readonly=True) - active = fields.Boolean(default=True) - variable_type = fields.Selection( - related="variable_id.variable_type", - readonly=True, - ) - option_id = fields.Many2one( - comodel_name="cx.tower.variable.option", - ondelete="restrict", - domain="[('variable_id', '=', variable_id)]", - ) - value_char = fields.Char( - string="Value", - compute="_compute_value_char", - inverse="_inverse_value_char", - store=True, - readonly=False, - ) - - # Direct model relations. - # Following functions should be updated when a new m2o field is added: - # - `_used_in_models()` - # - `_compute_is_global()`: add you field to 'depends' - # Define a `unique` constraint for new model too. - server_id = fields.Many2one( - comodel_name="cx.tower.server", index=True, ondelete="cascade" - ) - plan_line_action_id = fields.Many2one( - comodel_name="cx.tower.plan.line.action", index=True, ondelete="cascade" - ) - server_template_id = fields.Many2one( - comodel_name="cx.tower.server.template", index=True, ondelete="cascade" - ) - variable_ids = fields.Many2many( - comodel_name="cx.tower.variable", - relation="cx_tower_variable_value_variable_rel", - column1="variable_value_id", - column2="variable_id", - string="Variables", - compute="_compute_variable_ids", - store=True, - copy=False, - ) - required = fields.Boolean() - - _sql_constraints = [ - ( - "tower_variable_value_uniq", - "unique (variable_id, server_id, server_template_id, " - "plan_line_action_id, is_global)", - "Variable can be declared only once for the same record!", - ), - ( - "unique_variable_value_server", - "unique (variable_id, server_id)", - "A variable value cannot be assigned multiple times to the same server!", - ), - ( - "unique_variable_value_template", - "unique (variable_id, server_template_id)", - ( - "A variable value cannot be assigned multiple" - " times to the same server template!" - ), - ), - ( - "unique_variable_value_action", - "unique (variable_id, plan_line_action_id)", - ( - "A variable value cannot be assigned multiple" - " times to the same plan line action!" - ), - ), - ] - - # -- Compute fields -- - - @api.depends("variable_id", "variable_id.access_level") - def _compute_access_level(self): - """ - Automatically set the access_level based on Variable access level - """ - for rec in self: - if rec.variable_id: - rec.access_level = rec.variable_id.access_level - - @api.depends("server_id", "server_template_id", "plan_line_action_id") - def _compute_is_global(self): - """ - If variable considered `global` when it's not linked to any record. - """ - for rec in self: - rec.is_global = rec._check_is_global() - - @api.depends("option_id", "variable_id.option_ids") - def _compute_value_char(self): - """ - Compute the 'value_char' field, which holds the string representation - of the selected option for the variable. - """ - for rec in self: - if not rec.variable_id.option_ids: - rec.value_char = rec.value_char or False - rec.option_id = False - continue - if rec.option_id: - rec.value_char = rec.option_id.value_char - else: - rec.value_char = False - - @api.depends("value_char") - def _compute_variable_ids(self): - """ - Compute variable_ids based on value_char field. - """ - template_mixin_obj = self.env["cx.tower.template.mixin"] - for record in self: - record.variable_ids = template_mixin_obj._prepare_variable_commands( - ["value_char"], force_record=record - ) - - # -- Constraints -- - - @api.constrains("access_level", "variable_id") - def _check_access_level_consistency(self): - """ - Ensure that variable value access level is defined. - Ensure that the access level of the variable value is not lower than - the access level of the associated variable. - """ - access_level_dict = dict( - self.fields_get(["access_level"])["access_level"]["selection"] - ) - for rec in self: - if not rec.variable_id: - continue - if not rec.access_level: - raise ValidationError( - _( - "Access level is not defined for '%(variable)s'", - variable=rec.name, - ) - ) - if rec.access_level < rec.variable_id.access_level: - raise ValidationError( - _( - "The access level for Variable Value '%(value)s' " - "cannot be lower than the access level of its " - "Variable '%(variable)s'.\n" - "Variable Access Level: %(var_level)s\n" - "Variable Value Access Level: %(val_level)s", - value=rec.value_char, - variable=rec.variable_id.name, - var_level=access_level_dict[rec.variable_id.access_level], - val_level=access_level_dict[rec.access_level], - ) - ) - - @api.constrains("is_global", "value_char") - def _constraint_global_unique(self): - """Ensure that there is only one global value exist for the same variable - - Hint to devs: - `unique nulls not distinct (variable_id,server_id,global_id)` - can be used instead in PG 15.0+ - """ - for rec in self: - if rec.is_global: - val_count = self.search_count( - [("variable_id", "=", rec.variable_id.id), ("is_global", "=", True)] - ) - if val_count > 1: - # NB: there is a value check in tests for this message. - # Update `test_variable_value_toggle_global` - # if you modify this message in your code. - raise ValidationError( - _( - "Only one global value can be defined" - " for variable '%(var)s'", - var=rec.variable_id.name, - ) - ) - - @api.constrains("value_char", "option_id") - def _check_value_char_and_option_id(self): - """ - Check if the value_char is valid for the variable. - """ - for rec in self: - if not rec.variable_id: - continue - valid, message = rec.variable_id._validate_value(rec.value_char) - if not valid: - raise ValidationError(message) - if rec.option_id: - if rec.option_id.variable_id != rec.variable_id: - raise ValidationError( - _( - "Option '%(val)s' is not available for variable '%(var)s'", - val=rec.value_char, - var=rec.variable_id.name, - ) - ) - - @api.constrains("server_id", "server_template_id", "plan_line_action_id") - def _check_single_assignment(self): - """Ensure that a variable is only assigned to one model at a time.""" - for record in self: - # Check how many of the fields are set - count_assigned = ( - bool(record.server_id) - + bool(record.server_template_id) - + bool(record.plan_line_action_id) - ) - if count_assigned > 1: - raise ValidationError( - _( - "Variable '%(var)s' can only be assigned to one of the models " - "at a time: " - "Server, Server Template, or Plan Line Action.", - var=record.variable_id.name, - ) - ) - - # -- Onchange -- - - @api.onchange("variable_id") - def _onchange_variable_id(self): - """ - Reset option_id when variable changes or - doesn't have options - """ - for rec in self: - rec.update({"option_id": False, "value_char": False}) - - @api.onchange("value_char") - def _onchange_value_char(self): - """ - Check value before saving - """ - if not (self.variable_id and self.value_char): - return - try: - self.variable_id._validate_value(self.value_char) - except ValidationError as e: - return {"warning": {"title": _("Value is invalid"), "message": str(e)}} - - # -- Inverse -- - - def _inverse_is_global(self): - """Triggered when `is_global` is updated""" - global_values = self.filtered("is_global") - if global_values: - values_to_set = {} - - # Set m2o fields related to variable using models to 'False' - for related_model_info in self._used_in_models().values(): - m2o_field = related_model_info[0] - values_to_set.update({m2o_field: False}) - global_values.write(values_to_set) - - # Check if we are trying to remove 'global' from value - # that doesn't belong to any record. - record_related_values = self - global_values - for record in record_related_values: - if record._check_is_global(): - # NB: there is a value check in tests for this message. - # Update `test_variable_value_toggle_global` if you modify this message. - raise ValidationError( - _( - "Cannot change 'global' status for " - "'%(var)s' with value '%(val)s'." - "\nTry to assigns it to a record instead.", - var=record.variable_id.name, - val=record.value_char, - ) - ) - - def _inverse_value_char(self): - """Set option_id based on value_char""" - for rec in self: - if rec.variable_type == "o" and ( - not rec.option_id or rec.option_id.value_char != rec.value_char - ): - option = rec.variable_id.option_ids.filtered( - lambda x, v=rec.value_char: x.value_char == v - ) - rec.option_id = option and option.id - - # -- Create/write/unlink -- - - @api.model_create_multi - def create(self, vals_list): - """ - Workaround for the default value not being set - """ - variable_obj = self.env["cx.tower.variable"] - for vals in vals_list: - # Set access level from the variable - # if not provided explicitly - access_level = vals.get("access_level") - if access_level: - continue - variable_id = vals.get("variable_id") - if variable_id: - variable = variable_obj.browse(variable_id) - vals["access_level"] = variable.access_level - return super().create(vals_list) - - # -- Business logic -- - - def get_by_variable_reference( - self, - variable_reference, - server_id=None, - server_template_id=None, - check_global=True, - ): - """Get record based on its reference. - - Important: references are case sensitive! - - Args: - variable_reference (Char): variable reference - server_reference (Int): Server ID - server_template_reference (Int): Server template ID - - Returns: - Dict: Variable values that match provided reference - """ - - domain = [("variable_reference", "=", variable_reference)] - # Server or server template specific - if server_id: - domain.append(("server_id", "=", server_id)) - elif server_template_id: - domain.append(("server_template_id", "=", server_template_id)) - - if check_global: - domain = OR( - [ - domain, - [ - ("variable_reference", "=", variable_reference), - ("is_global", "=", True), - ], - ] - ) - - search_result = self.search(domain) - result = {} - if search_result: - if server_id: - value_char = search_result.filtered("server_id").mapped("value_char") - result.update( - {"server": value_char and value_char[0] if value_char else None} - ) - if server_template_id: - value_char = search_result.filtered("server_template_id").mapped( - "value_char" - ) - result.update( - { - "server_template": value_char and value_char[0] - if value_char - else None - } - ) - if check_global: - value_char = search_result.filtered("is_global").mapped("value_char") - result.update( - {"global": value_char and value_char[0] if value_char else None} - ) - - return result - - def _used_in_models(self): - """Returns information about models which use this mixin. - - Returns: - dict(): of the following format: - {"model.name": ("m2o_field_name", "model_description")} - Eg: - {"my.custom.model": ("much_model_id", "Much Model")} - """ - return { - "cx.tower.server": ("server_id", "Server"), - "cx.tower.plan.line.action": ("plan_line_action_id", "Action"), - "cx.tower.server.template": ("server_template_id", "Server Template"), - } - - def _check_is_global(self): - """ - This is a helper function used to define - which variables are considered 'Global' - Override it to implement your custom logic. - - Returns: - bool: True if global else False - """ - - self.ensure_one() - is_global = True - - # Get m2o field values for all models that use variables. - # If none of them is set such value is considered 'global'. - for related_model_info in self._used_in_models().values(): - m2o_field = related_model_info[0] - if self[m2o_field]: - is_global = False - break - return is_global - - def _get_extra_vals_fields(self): - """Check cx.tower.reference.mixin for the function documentation""" - - # Use _used_in_models as a source of truth - return [fld_val[0] for fld_val in self._used_in_models().values()] - - def _pre_populate_references(self, model_name, field_name, vals_list): - """ - Generate model-scoped references for variable values. - - Overrides the mixin method to implement a model-dependent reference pattern. - - Pattern: - ___ - Global: - __global - """ - # Collect parent variable references - parent_record_refs = self._prepare_references(model_name, field_name, vals_list) - model_reference = self._get_model_generic_reference() - - # Prepare mappings for linked models defined in _used_in_models - used_models = self._used_in_models() or {} - # Map m2o field -> model name - m2o_to_model = {info[0]: model for model, info in used_models.items()} - # Precompute linked model generic refs and record refs - linked_generic_by_field = {} - linked_refs_by_field = {} - for model, (m2o_field, _desc) in used_models.items(): - linked_generic_by_field[m2o_field] = self.env[ - model - ]._get_model_generic_reference() - linked_refs_by_field[m2o_field] = self._prepare_references( - model, m2o_field, vals_list - ) - - for vals in vals_list: - # Respect explicitly provided references with at least one valid symbol - existing_reference = vals.get("reference") - if existing_reference and bool( - re.search(self.REFERENCE_PRELIMINARY_PATTERN, existing_reference) - ): - continue - - variable_id = vals.get(field_name) - variable_reference = parent_record_refs.get(variable_id) - if not variable_reference: - # Fallback to generic variable reference if parent reference missing - variable_reference = self.env[model_name]._get_model_generic_reference() - - # Determine which related model the value is linked to - linked_m2o_field = next( - (f for f in m2o_to_model.keys() if vals.get(f)), None - ) - - if linked_m2o_field: - linked_model_generic = linked_generic_by_field.get(linked_m2o_field) - linked_record_id = vals.get(linked_m2o_field) - linked_record_reference = linked_refs_by_field.get( - linked_m2o_field, {} - ).get(linked_record_id) - vals["reference"] = ( - f"{variable_reference}_" - f"{model_reference}_" - f"{linked_model_generic}_" - f"{linked_record_reference}" - ) - else: - # Global value (not linked to any record) - vals["reference"] = f"{variable_reference}_{model_reference}_global" - - return vals_list - - def _get_pre_populated_model_data(self): - """Check cx.tower.reference.mixin for the function documentation""" - res = super()._get_pre_populated_model_data() - res.update({"cx.tower.variable.value": ["cx.tower.variable", "variable_id"]}) - return res diff --git a/addons/cetmix_tower_server/models/cx_tower_vault.py b/addons/cetmix_tower_server/models/cx_tower_vault.py deleted file mode 100644 index 5fedf56..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_vault.py +++ /dev/null @@ -1,52 +0,0 @@ -from odoo import fields, models - -from odoo.addons.rpc_helper.decorator import disable_rpc - - -@disable_rpc() -class CxTowerVault(models.Model): - """Vault for storing secret data. - - This model is used to store secret data for various resources. - - The data is stored in the database and can be accessed using the - `_get_secret_values` method. - - Do not use this model directly, use the `VaultMixin` instead. - """ - - _name = "cx.tower.vault" - _description = "Cetmix Tower Vault" - - res_model = fields.Char( - string="Resource Model", - required=True, - copy=False, - help="Model name of the resource that uses this vault", - ) - res_id = fields.Many2oneReference( - string="Resource ID", - model_field="res_model", - help="ID of the resource that uses this vault", - required=True, - copy=False, - ) - field_name = fields.Char( - required=True, - help="Name of the field that contains the secret value", - copy=False, - ) - data = fields.Text( - string="Secret Data", - required=True, - copy=False, - help="The secret data to be stored in the vault", - ) - - _sql_constraints = [ - ( - "vault_unique_key", - "UNIQUE(res_model, res_id, field_name)", - "Each secret (model, record, field) must be unique in the vault.", - ), - ] diff --git a/addons/cetmix_tower_server/models/cx_tower_vault_mixin.py b/addons/cetmix_tower_server/models/cx_tower_vault_mixin.py deleted file mode 100644 index 08a4b17..0000000 --- a/addons/cetmix_tower_server/models/cx_tower_vault_mixin.py +++ /dev/null @@ -1,408 +0,0 @@ -from collections import defaultdict - -from odoo import api, models - - -class CxTowerVaultMixin(models.AbstractModel): - """Mixin for vault functionality. - - This mixin provides methods to securely store and retrieve sensitive data - in the vault. Inheriting models must define SECRET_FIELDS list with field - names that should be stored in the vault. - """ - - _name = "cx.tower.vault.mixin" - _description = "Cetmix Tower Vault Mixin" - - SECRET_VALUE_PLACEHOLDER = "*****" - SECRET_FIELDS = [] - - def _read(self, fields): # pylint: disable=missing-return # doesn't return anything - """Substitute fields based on api. - - This method replaces values of secret fields with a placeholder value - when they are read from the database. - - Args: - fields (list): List of fields to read - """ - super()._read(fields) - - show_all = not fields - secret_fields = ( - self.SECRET_FIELDS - if show_all - else [f for f in self.SECRET_FIELDS if f in fields] - ) - - for record in self: - for secret_field in secret_fields: - try: - record._cache[secret_field] = self.SECRET_VALUE_PLACEHOLDER - except Exception: # pylint: disable=except-pass - # skip SpecialValue - # (e.g. for missing record or access right) - pass - - @api.model_create_multi - def create(self, vals_list): - """Override create to handle secret values securely. - - Extracts secret fields, stores them in vault, and prevents - actual secret values from being saved in the main table. - - Args: - vals_list (list): List of dictionaries containing field values - for record creation - - Returns: - recordset: Created records with secret values stored in vault - - Note: - Secret fields are automatically processed and stored securely. - The main database table never contains actual secret values. - """ - - # Step 1: Extract secret fields and generate temporary IDs - secret_vals = self._extract_and_replace_secret_fields(vals_list) - - # Step 2: Create records with batch operation - records = super().create(vals_list) - - # Step 3: Update vault records with real IDs - if secret_vals: - self._process_secret_values_after_creation(records, secret_vals) - - return records - - def write(self, vals): - """Override write to handle secret fields. - - Extracts secret field values from vals dictionary and stores them securely - in the vault instead of the main database table. The remaining non-secret - fields are processed by the standard write method. - - Args: - vals (dict): Dictionary of field values to write to records - - Returns: - bool: Result of the parent write operation - - Note: - Secret fields defined in SECRET_FIELDS are automatically intercepted - and stored in vault. Cache is invalidated for all secret fields when - any secret field is modified. - """ - # Extract secret fields - secret_values = {} - for secret_field in self.SECRET_FIELDS: - if secret_field in vals: - secret_values[secret_field] = vals.pop(secret_field) - - res = super().write(vals) - - if secret_values: - self._set_secret_values(secret_values) - # Invalidate cache for all secret fields - self.invalidate_recordset(self.SECRET_FIELDS) - - return res - - def unlink(self): - """Override unlink to delete vault records. - - Automatically removes all associated vault records after deleting - the main records to prevent orphaned secret data in the vault. - - Returns: - bool: Result of the parent unlink operation - - Note: - Vault cleanup is performed automatically and cannot be bypassed. - """ - ids = self.ids - - res = super().unlink() - - # Find all vault records for these records - vault_records = ( - self.env["cx.tower.vault"] - .sudo() - .search([("res_model", "=", self._name), ("res_id", "in", ids)]) - ) - - # Delete vault records - if vault_records: - vault_records.sudo().unlink() - - return res - - def _get_secret_value(self, field_name): - """Retrieves the actual secret value for a specific field for a single record. - - This method is the only way to get the real secret field value because: - - Direct field access (e.g., self.secret_field) - returns placeholder due to _read() override - - The actual field in the main table is empty/NULL - as values are stored in vault - - Args: - field_name (str): Name of the secret field to retrieve - - Returns: - str or None: The actual secret value, or None if not found or field - is not in SECRET_FIELDS - - Note: - This method bypasses Odoo's ORM field access to avoid getting - placeholder values returned by the overridden _read() method. - """ - - self.ensure_one() - - return self._get_secret_values([field_name]).get(self.id, {}).get(field_name) - - def _get_secret_values(self, fields_list=None): - """Retrieve secret values from the vault for specified fields. - - This method fetches secret values stored in the vault for all records - in the current recordset and specified fields (or all SECRET_FIELDS). - - Args: - fields_list (list, optional): List of field names to retrieve. - Defaults to all SECRET_FIELDS. - - Returns: - dict: Dictionary mapping record IDs to their secret field values. - Structure: {res_id: {field_name: secret_value}} - - Example: - {1: {'ssh_password': 'secret123', 'host_key': 'key456'}, - 2: {'ssh_password': 'secret789'}} - - Note: - This method searches vault records using standard domain filtering - by res_id, and field_name for reliable record matching. - If a record has no secret values this record is not included in the result. - """ - # If no records, return empty dict - if not self: - return {} - - # Prepare fields to fetch - fields_to_fetch = ( - [f for f in fields_list if f in self.SECRET_FIELDS] - if fields_list - else self.SECRET_FIELDS - ) - # If no fields to fetch, return empty dict - if not fields_to_fetch: - return {} - - # Search vault records for all records and all secret fields - domain = [ - ("res_model", "=", self._name), - ("res_id", "in", self.ids), - ("field_name", "in", fields_to_fetch), - ] - vault_records = ( - self.env["cx.tower.vault"] - .sudo() - .search_read( - domain, - ["res_id", "field_name", "data"], - ) - ) - res = defaultdict(dict) - for record in vault_records: - res[record["res_id"]][record["field_name"]] = record["data"] - - return dict(res) - - def _set_secret_values(self, vals): - """Store secret values in the vault. - - This method stores sensitive data in the vault for all records in the recordset. - It either updates existing vault records or creates new ones for each - record-field pair in the vals dictionary. - - This method can be overridden to implement custom storage mechanisms - for secret values, such as external key management systems or - encryption services. - - Args: - vals (dict): Dictionary mapping field names to their secret values - to be stored in the vault for all records - - Returns: - None - """ - if not vals or not self: - return - - # Get all existing vault records in ONE SQL query - domain = [ - ("res_model", "=", self._name), - ("res_id", "in", self.ids), - ("field_name", "in", list(vals.keys())), - ] - existing_vault_records = self.env["cx.tower.vault"].sudo().search(domain) - - # Prepare data for batch operations - vals_to_update_records = defaultdict(lambda: self.env["cx.tower.vault"]) - records_to_unlink = self.env["cx.tower.vault"] - records_to_create = [] - - # Index existing records by (res_id, field_name) for O(1) lookups - existing_map = {(v.res_id, v.field_name): v for v in existing_vault_records} - - # Only allow known secret fields to be set - allowed_fields = set(self.SECRET_FIELDS) - - # Process each record and field combination - for record in self: - for field, value in vals.items(): - if field not in allowed_fields: - continue - # Fast lookup for existing record - existing_record = existing_map.get((record.id, field)) - if existing_record: - if value is False or value is None: - records_to_unlink |= existing_record - else: - vals_to_update_records[value] |= existing_record - - else: - if value is False or value is None: - continue - - records_to_create.append( - { - "res_model": self._name, - "res_id": record.id, - "field_name": field, - "data": value, - } - ) - - # Batch operations - for value, records in vals_to_update_records.items(): - records.sudo().write({"data": value}) - - if records_to_create: - self.env["cx.tower.vault"].sudo().create(records_to_create) - if records_to_unlink: - records_to_unlink.sudo().unlink() - - def _extract_and_replace_secret_fields(self, vals_list): - """Extract secret fields and replace with temporary identifiers. - - Processes value dictionaries for record creation, replacing secret field values - with unique temporary identifiers. The actual secret values are mapped to these - temporary identifiers for later secure storage in the vault system. - - Args: - vals_list (list): List of value dictionaries for record creation. - - Returns: - dict: Mapping of temporary identifiers to secret values. - Note: vals_list is modified in-place to contain temp identifiers. - - Note: - Used during record creation as part of the secure secret storage workflow. - """ - temp_id_counter = 0 - secret_vals = {} - - for vals in vals_list: - for secret_field in self.SECRET_FIELDS: - if ( - secret_field in vals - and vals[secret_field] is not False - and vals[secret_field] is not None - ): - temp_id_counter += 1 - temp_identifier = str(temp_id_counter) - secret_vals[temp_identifier] = vals[secret_field] - vals[secret_field] = temp_identifier - - return secret_vals - - def _process_secret_values_after_creation(self, records, secret_vals): - """Process secret values after records creation. - - Replaces temporary identifiers with actual secret values in the vault - and invalidates cache for affected fields. - - Args: - records (recordset): Newly created records with temporary identifiers - secret_vals (dict): Mapping of temporary identifiers to secret values - - Returns: - None - - Note: - Called automatically during create() process. Should not be used directly. - """ - fields_str = ", ".join(self.SECRET_FIELDS) - query = f"SELECT id, {fields_str} FROM {self._table} WHERE id in %s" - self.env.cr.execute(query, (tuple(records.ids),)) - records_dict = self.env.cr.dictfetchall() - - for record_dict in records_dict: - self._process_single_record_secrets(record_dict, secret_vals) - - records._clear_temp_values() - records.invalidate_recordset(self.SECRET_FIELDS) - - def _process_single_record_secrets(self, record_dict, secret_vals): - """Process secrets for a single record. - - Replaces temporary identifiers with actual secret values for one record, - clears temporary values from main table and stores secrets in vault. - - Args: - record_dict (dict): Dictionary with record data - including temporary identifiers - secret_vals (dict): Mapping of temporary identifiers to actual secret values - - Returns: - None - - Note: - Internal method used by _process_secret_values_after_creation. - """ - record_id = record_dict.get("id") - vault_vals = {} - field_temp_id_pairs = ( - (field_name, record_dict[field_name]) for field_name in self.SECRET_FIELDS - ) - - # Collect secret values and fields to clear - for field_name, temp_identifier in field_temp_id_pairs: - secret_value = secret_vals.get(temp_identifier) - if secret_value: - vault_vals[field_name] = secret_value - - # Update database and vault if needed - if vault_vals: - record = self.browse(record_id) - record._set_secret_values(vault_vals) - - def _clear_temp_values(self): - """Clear temporary values from main table. - - Sets all SECRET_FIELDS to NULL in the database to remove temporary - identifiers after secret values have been stored in vault. - Works with multiple records in the recordset. - - Returns: - None - - Note: - Internal method used during secret processing workflow. - Clears all SECRET_FIELDS for all records in the current recordset. - """ - set_clause = ", ".join(f"{field} = NULL" for field in self.SECRET_FIELDS) - query = f"UPDATE {self._table} SET {set_clause} WHERE id in %s" - self.env.cr.execute(query, (tuple(self.ids),)) diff --git a/addons/cetmix_tower_server/models/ir_actions_server.py b/addons/cetmix_tower_server/models/ir_actions_server.py deleted file mode 100644 index c3bd1d2..0000000 --- a/addons/cetmix_tower_server/models/ir_actions_server.py +++ /dev/null @@ -1,24 +0,0 @@ -from odoo import _, models -from odoo.exceptions import AccessError - - -class IrActionsServer(models.Model): - _inherit = "ir.actions.server" - - def run(self): - """ - We override this method to return more - user friendly error messages. - """ - if self.sudo().model_name == "cx.tower.server": - try: - res = super().run() - return res - except AccessError as e: - raise AccessError( - _( - "You need to have 'write' access to all servers " - "you want to run this action on." - ) - ) from e - return super().run() diff --git a/addons/cetmix_tower_server/models/res_config_settings.py b/addons/cetmix_tower_server/models/res_config_settings.py deleted file mode 100644 index 36da516..0000000 --- a/addons/cetmix_tower_server/models/res_config_settings.py +++ /dev/null @@ -1,79 +0,0 @@ -from odoo import _, fields, models -from odoo.exceptions import ValidationError - - -class ResConfigSettings(models.TransientModel): - """ - Inherit res.config.settings to add new settings - """ - - _inherit = "res.config.settings" - - cetmix_tower_command_timeout = fields.Integer( - string="Command Timeout", - config_parameter="cetmix_tower_server.command_timeout", - help="Timeout for commands in seconds after which" - " the command will be terminated", - ) - cetmix_tower_notification_type_error = fields.Selection( - string="Error Notifications", - selection=lambda self: self._selection_notifications_type(), - config_parameter="cetmix_tower_server.notification_type_error", - help="Type of error notifications", - ) - cetmix_tower_notification_type_success = fields.Selection( - string="Success Notifications", - selection=lambda self: self._selection_notifications_type(), - config_parameter="cetmix_tower_server.notification_type_success", - help="Type of success notifications", - ) - - def _selection_notifications_type(self): - """ - Selection of notifications type - """ - return [ - ("sticky", _("Sticky")), - ("non_sticky", _("Non-sticky")), - ] - - def action_configure_cron_pull_files_from_server(self): - """ - Configure cron job to pull files from server - """ - return self._get_cron_job_action( - "cetmix_tower_server.ir_cron_auto_pull_files_from_server" - ) - - def action_configure_zombie_commands_cron(self): - """ - Configure cron job to check zombie commands - """ - return self._get_cron_job_action( - "cetmix_tower_server.ir_cron_check_zombie_commands" - ) - - def action_configure_run_scheduled_tasks_cron(self): - """ - Configure cron job to run scheduled tasks - """ - return self._get_cron_job_action( - "cetmix_tower_server.ir_cron_run_scheduled_tasks" - ) - - def _get_cron_job_action(self, cron_xml_id): - """ - Get action to configure cron job - """ - self.ensure_one() - cron_id = self.env.ref(cron_xml_id).id - if not cron_id: - raise ValidationError(_("Cron job not found")) - return { - "name": _("Cron Job"), - "views": [(False, "form")], - "res_model": "ir.cron", - "res_id": cron_id, - "type": "ir.actions.act_window", - "target": "new", - } diff --git a/addons/cetmix_tower_server/models/res_partner.py b/addons/cetmix_tower_server/models/res_partner.py deleted file mode 100644 index f25f2d2..0000000 --- a/addons/cetmix_tower_server/models/res_partner.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, fields, models - - -class ResPartner(models.Model): - _inherit = "res.partner" - - server_ids = fields.One2many( - "cx.tower.server", - "partner_id", - string="Servers", - groups="cetmix_tower_server.group_user", - ) - - server_count = fields.Integer( - compute="_compute_server_count", - recursive=True, - ) - - secret_ids = fields.One2many( - "cx.tower.key.value", - "partner_id", - string="Secrets", - domain=[("key_id.key_type", "=", "s")], - groups="cetmix_tower_server.group_manager", - ) - - @api.depends("server_ids", "child_ids.server_count") - def _compute_server_count(self): - for partner in self: - own_server_count = len(partner.server_ids) - child_server_count = sum(partner.child_ids.mapped("server_count")) - partner.server_count = own_server_count + child_server_count - - def action_view_partner_servers(self): - """Open server list filtered by partner and all its descendants.""" - self.ensure_one() - return { - "name": "Servers", - "type": "ir.actions.act_window", - "res_model": "cx.tower.server", - "view_mode": "kanban,tree,form", - "domain": [("partner_id", "child_of", self.id)], - "context": {"default_partner_id": self.id}, - } diff --git a/addons/cetmix_tower_server/models/tools.py b/addons/cetmix_tower_server/models/tools.py deleted file mode 100644 index 41b0f80..0000000 --- a/addons/cetmix_tower_server/models/tools.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from random import choices - -CHARS = "23456789acefhjkmnprtvwxyz" - - -def generate_random_id(sections=1, population=4, separator="-"): - """Generates random id - eg 'ahj2-jer83' - - Args: - sections (int, optional): number of sections. Defaults to 1. - population (int, optional): number of symbols per section. Defaults to 4. - separator (str, optional): section separator. Defaults to "-". - - Returns: - Str: generated id - """ - if sections < 1 or population < 0: - return None - - def get_section(): - return "".join(choices(CHARS, k=population)) - - # Single section - if sections == 1: - return get_section() - - # Multiple sections - result = [] - for _ in range(sections): - result.append(get_section()) - - return separator.join(result) diff --git a/addons/cetmix_tower_server/pyproject.toml b/addons/cetmix_tower_server/pyproject.toml deleted file mode 100644 index 4231d0c..0000000 --- a/addons/cetmix_tower_server/pyproject.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build-system] -requires = ["whool"] -build-backend = "whool.buildapi" diff --git a/addons/cetmix_tower_server/readme/CONFIGURE.md b/addons/cetmix_tower_server/readme/CONFIGURE.md deleted file mode 100644 index 8c717e5..0000000 --- a/addons/cetmix_tower_server/readme/CONFIGURE.md +++ /dev/null @@ -1 +0,0 @@ -Please refer to the [official documentation](https://cetmix.com/tower) for detailed configuration instructions. diff --git a/addons/cetmix_tower_server/readme/DESCRIPTION.md b/addons/cetmix_tower_server/readme/DESCRIPTION.md deleted file mode 100644 index 51ca687..0000000 --- a/addons/cetmix_tower_server/readme/DESCRIPTION.md +++ /dev/null @@ -1,4 +0,0 @@ -[Cetmix Tower](https://cetmix.com/tower) offers a streamlined solution for managing remote servers and applications via SSH or API calls directly from [Odoo](https://odoo.com). -It is designed for versatility across different operating systems and software environments, providing a practical option for those looking to manage servers without getting tied down by vendor or technology constraints. - -Please refer to the [official documentation](https://cetmix.com/tower) for detailed information. diff --git a/addons/cetmix_tower_server/readme/HISTORY.md b/addons/cetmix_tower_server/readme/HISTORY.md deleted file mode 100644 index 02dbe5b..0000000 --- a/addons/cetmix_tower_server/readme/HISTORY.md +++ /dev/null @@ -1,215 +0,0 @@ -## 16.0.2.2.8 (2025-12-22) - -- Bugfixes: Handle malformed expressions in flight plan line conditions. (5154) - - -## 16.0.2.2.7 (2025-12-16) - -- Features: Support for ANSI formatting in server logs. (5141) - -- Bugfixes: UI/UX fixed and improvements. (5141) - - -## 16.0.2.2.6 (2025-12-11) - -- Features: Improve search views, implement the search panel for selected views. (5139) - - -## 16.0.2.2.5 (2025-12-10) - -- Bugfixes: Custom values in flight plan are lost in a skipped command and are not available after it. (5129) - - -## 16.0.2.2.4 (2025-12-10) - -- Features: Parse empty or missing key values as 'None' instead of leaving key reference as is. (5134) - - -## 16.0.2.2.3 (2025-12-03) - -- Bugfixes: Save correct error message in log when SSH connection fails. (5109) - - -## 16.0.2.2.2 (2025-12-03) - -- Bugfixes: Make variables selectable in scheduled tasks (5105) - - -## 16.0.2.2.0 (2025-11-12) - -- Features: Integrate user notifications into the main module, drop the 'cetmix_tower_notify_backend' module. (5074) - - -## 16.0.2.0.6 (2025-10-27) - -- Features: Tag mixin and helper commands. (5039) - - -## 16.0.2.0.5 (2025-10-16) - -- Bugfixes: Flight plan command exception handling (4930) - - -## 16.0.2.0.4 (2025-10-13) - -- Features: Auto update references for related records (5005) - - -## 16.0.2.0.3 (2025-10-13) - -- Features: Terminate running flight plan manually (3410) - - -## 16.0.2.0.2 (2025-10-08) - -- Features: UI/UX improvements (4996) - -- Bugfixes: Handle secret values when a record is duplicated using copy() (4996) - - -## 16.0.2.0.1 (2025-10-08) - -- Bugfixes: Improve variable value references uniqueness (4961) - - -## 16.0.2.0.0 (2025-10-07) - -- Features: 'Cetmix Tower Vault' - new way of centralized password/key management (4824) - - -## 16.0.1.7.2 (2025-09-18) - -- Features: Set 'Auto Sync' in files from file templates (4949) - - -## 16.0.1.7.1 (2025-09-10) - -- Bugfixes: Check custom values in flight plan line condition (4922) - - -## 16.0.1.6.4 (2025-08-18) - -- Features: Improve the extendability of the file upload command. (4759) - - -## 16.0.1.6.3 (2025-08-13) - -- Features: Improve access settings for logs (4866) - - -## 16.0.1.6.2 (2025-08-05) - -- Bugfixes: Pin paramiko version to "<4" to maintain compatibility with legacy installations (4891) - - -## 16.0.1.6.0 (2025-07-30) - -- Features: Optional behaviour when file uploaded by command already exists on the server. (4740) - - -## 16.0.1.5.3 (2025-07-29) - -- Features: Make file references server dependent to be more unique (4870) - - -## 16.0.1.5.1 (2025-07-25) - -- Features: Select secrets from dropdown list in the code fields (4853) - - -## 16.0.1.5.0 (2025-07-22) - -- Features: Select variables from dropdown list in the code fields (4827) - - -## 16.0.1.3.0 (2025-07-17) - -- Features: Add the tldextract and dnspython libraries. (4737) - - -## 16.0.1.1.4 (2025-07-07) - -- Bugfixes: Command log sorting (4816) - - -## 16.0.1.1.2 (2025-06-25) - -- Features: Required variables in servers (4779) - - -## 16.0.1.1.1 (2025-06-21) - -- Features: Command view improvements (4753) - - -## 16.0.1.1.0 (2025-06-20) - -- Features: Run commands and flight plans using scheduled tasks. (4650) - - -## 16.0.1.0.12 (2025-06-06) - -- Features: Improve command and flight plan log management. (4749) - - -## 16.0.1.0.11 (2025-06-06) - -- Bugfixes: Host key cannot be retrieved from the UI. (4747) - - -## 16.0.1.0.10 (2025-05-24) - -- Features: Improve command log and flight plan form views (4697) - - -## 16.0.1.0.9 (2025-05-23) - -- Bugfixes: Error when rendering a file not attached to a server. (4715) - - -## 16.0.1.0.8 (2025-05-21) - -- Features: References for secret values. (4696) -- Features: Make the "Host key" field non-required in the form view to improve the UX. (4699) - - -## 16.0.1.0.7 (2025-05-16) - -- Features: Option to preserve command splitting when using sudo​. (4641) -- Features: Record references for files. (4670) -- Features: Use `sudo` parameter to pass sudo mode to command runner instead of using context. (4678) - -- Bugfixes: Incorrect sudo usage in commands run in wizard. Pass 'No split for sudo' property to commands run in wizard. (4679) - - -## 16.0.1.0.6 (2025-05-16) - -- Features: Improve the key storage functionality. (4686) - - -## 16.0.1.0.5 (2025-05-09) - -- Bugfixes: Non-critical issues and performance improvements. (4663) - - -## 16.0.1.0.4 (2025-04-30) - -- Features: UI/UX improvements. (4642) - - -## 16.0.1.0.3 (2025-04-22) - -- Features: Allow to pass custom variable values to commands (4524) -- Features: Cetmix Tower Odoo Automation model: pass custom variable values to the `server_run_command` method. (4547) - -- Bugfixes: Random id generation, sudo command parsing, record rule names, spelling errors in descriptions. (4612) - - -## 16.0.1.0.2 (2025-04-22) - -- Bugfixes: Refactor secret value handling, fix the new server template creation wizard. (4601) - - -## 16.0.1.0.1 - -Release for Odoo 16.0 diff --git a/addons/cetmix_tower_server/readme/USAGE.md b/addons/cetmix_tower_server/readme/USAGE.md deleted file mode 100644 index 901f5a6..0000000 --- a/addons/cetmix_tower_server/readme/USAGE.md +++ /dev/null @@ -1 +0,0 @@ -Please refer to the [official documentation](https://cetmix.com/tower) for detailed usage instructions. diff --git a/addons/cetmix_tower_server/readme/newsfragments/.gitkeep b/addons/cetmix_tower_server/readme/newsfragments/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/addons/cetmix_tower_server/security/cetmix_tower_server_groups.xml b/addons/cetmix_tower_server/security/cetmix_tower_server_groups.xml deleted file mode 100644 index 39e328e..0000000 --- a/addons/cetmix_tower_server/security/cetmix_tower_server_groups.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Cetmix Tower - 199 - - - - - Access Level - - - - User - - - Basic actions for selected servers. - - - - - Manager - - - - Create and modify selected servers. - - - - - Root - - - - Full control over all servers. - - - - - - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_command_log_security.xml b/addons/cetmix_tower_server/security/cx_tower_command_log_security.xml deleted file mode 100644 index 6deb00a..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_command_log_security.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - Tower command log: user access rule - - - [ - ("access_level", "=", "1"), - ("create_uid", "=", user.id), - ("server_id.user_ids", "in", [user.id]) - ] - - - - Tower command log: manager access rule - - - [ - "&", - ("access_level", "<=", "2"), - "|", - ("server_id.user_ids", "in", [user.id]), - ("server_id.manager_ids", "in", [user.id]) - ] - - - - - Tower command log: root access rule - - [(1, "=", 1)] - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_command_security.xml b/addons/cetmix_tower_server/security/cx_tower_command_security.xml deleted file mode 100644 index 2903c82..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_command_security.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - Command: User read - - - - ["&", - ("access_level", "=", "1"), - "|", - ("user_ids", "in", [user.id]), - ("server_ids.user_ids", "in", [user.id]) - ] - - - - - - - - - - Command: Manager read - - - - ["&", - ("access_level", "<=", "2"), - "|", - "|", ("user_ids", "in", [user.id]), ("manager_ids", "in", [user.id]), - "|", - ("server_ids", "=", False), - "|", - ("server_ids.user_ids", "in", [user.id]), - ("server_ids.manager_ids", "in", [user.id]) - ] - - - - - - - - - - Command: Manager write & create - - - - [("access_level", "<=", "2"), ("manager_ids", "in", [user.id])] - - - - - - - - - - Command: Manager unlink - - - - [ - ("access_level", "<=", "2"), - ("create_uid", "=", user.id), - ("manager_ids", "in", [user.id]) - ] - - - - - - - - - - Command: Root unrestricted access - - - [(1, '=', 1)] - - diff --git a/addons/cetmix_tower_server/security/cx_tower_file_security.xml b/addons/cetmix_tower_server/security/cx_tower_file_security.xml deleted file mode 100644 index 6401136..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_file_security.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - File: User read via related server (user_ids) - - - [('server_id.user_ids', 'in', [user.id])] - - - - - - - - - File: Manager write & create via related server (manager_ids) - - - [('server_id.manager_ids', 'in', [user.id])] - - - - - - - - - File: Manager unlink via related server (manager_ids) and record creator - - - - [ ('server_id.manager_ids', 'in', [user.id]), ('create_uid', '=', user.id) ] - - - - - - - - - - File: Root Unrestricted Access - - - [(1, '=', 1)] - - diff --git a/addons/cetmix_tower_server/security/cx_tower_file_template_security.xml b/addons/cetmix_tower_server/security/cx_tower_file_template_security.xml deleted file mode 100644 index eee1c5e..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_file_template_security.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - File: Manager read (user_ids or manager_ids) - - - - ["|", ("user_ids", "in", [user.id]), ("manager_ids", "in", [user.id])] - - - - - - - - - - File: Manager write & create (manager_ids) - - - [('manager_ids', 'in', [user.id])] - - - - - - - - - File: Manager unlink (manager_ids & creator) - - - - [("manager_ids", "in", [user.id]), ("create_uid", "=", user.id)] - - - - - - - - - - File: Root unrestricted access - - - [(1, '=', 1)] - - diff --git a/addons/cetmix_tower_server/security/cx_tower_key_security.xml b/addons/cetmix_tower_server/security/cx_tower_key_security.xml deleted file mode 100644 index 5f53277..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_key_security.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Key: Manager Read Access - Users/Managers - - ['|', ('user_ids', 'in', [user.id]), ('manager_ids', 'in', [user.id])] - - - - - - - - - Key: Manager Read Access - Secret Type - - [('key_type', '=', 's')] - - - - - - - - - Key: Manager Read Access - SSH Key - - [('key_type', '=', 'k'), '|', - ('server_ssh_ids.user_ids', 'in', [user.id]), - ('server_ssh_ids.manager_ids', 'in', [user.id])] - - - - - - - - - - Key: Manager Write/Create Access - Managers - - [('manager_ids', 'in', [user.id])] - - - - - - - - - Key: Manager Write/Create Access - SSH Key - - ['&', ('key_type', '=', 'k'), - ('server_ssh_ids.manager_ids', 'in', [user.id])] - - - - - - - - - - Key: Manager Delete Access - Managers - - [('manager_ids', 'in', [user.id]), ('create_uid', '=', user.id)] - - - - - - - - - Key: Manager Delete Access - SSH Key - - [('key_type', '=', 'k'), - ('server_ssh_ids.manager_ids', 'in', [user.id]), - ('create_uid', '=', user.id)] - - - - - - - - - - Key: Root Full Access - - [(1, '=', 1)] - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_key_value_security.xml b/addons/cetmix_tower_server/security/cx_tower_key_value_security.xml deleted file mode 100644 index 120309c..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_key_value_security.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - Key Value: Manager Read Access - Key Users/Managers - - ['|', ('key_id.user_ids', 'in', [user.id]), ('key_id.manager_ids', 'in', [user.id])] - - - - - - - - - Key Value: Manager Read Access - Server Users/Managers - - [('key_id.key_type', '=', 's'), - '|', '|', ('server_id', '=', False), - ('server_id.user_ids', 'in', [user.id]), - ('server_id.manager_ids', 'in', [user.id])] - - - - - - - - - - Key Value: Manager Write/Create Access - Key Managers - - [('key_id.manager_ids', 'in', [user.id])] - - - - - - - - - Key Value: Manager Write/Create Access - Server Managers - - [('server_id.manager_ids', 'in', [user.id])] - - - - - - - - - - Key Value: Manager Delete Access - Key Managers - - [('key_id.key_type', '=', 's'),('key_id.manager_ids', 'in', [user.id]), ('create_uid', '=', user.id)] - - - - - - - - - Key Value: Manager Delete Access - Server Managers - - [('key_id.key_type', '=', 's'),('server_id.manager_ids', 'in', [user.id]), ('create_uid', '=', user.id)] - - - - - - - - - - Key Value: Root Full Access - - [(1, '=', 1)] - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_plan_line_action_security.xml b/addons/cetmix_tower_server/security/cx_tower_plan_line_action_security.xml deleted file mode 100644 index 39f6c50..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_plan_line_action_security.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - Plan Line Action: User read - - - - ["&", - ("access_level", "=", "1"), - "|", - ("plan_id.user_ids", "in", [user.id]), - ("plan_id.server_ids.user_ids", "in", [user.id]) - ] - - - - - - - - - - Plan Line Action: Manager read - - - - - ["&", - ("access_level", "<=", "2"), - "|", - "|", ("plan_id.user_ids", "in", [user.id]), ("plan_id.manager_ids", "in", [user.id]), - "|", - ("plan_id.server_ids", "=", False), - "|", - ("plan_id.server_ids.user_ids", "in", [user.id]), - ("plan_id.server_ids.manager_ids", "in", [user.id]) - ] - - - - - - - - - - Plan Line Action: Manager write & create - - - - ["&", ("access_level", "<=", "2"), ("plan_id.manager_ids", "in", [user.id])] - - - - - - - - - - Plan Line Action: Manager unlink - - - - [ - ("access_level", "<=", "2"), - ("create_uid", "=", user.id), - ("plan_id.manager_ids", "in", [user.id]) - ] - - - - - - - - - - Plan Line Action: Root unrestricted access - - - [(1, '=', 1)] - - diff --git a/addons/cetmix_tower_server/security/cx_tower_plan_line_security.xml b/addons/cetmix_tower_server/security/cx_tower_plan_line_security.xml deleted file mode 100644 index 0e844f2..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_plan_line_security.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - Plan Line: User read - - - - ["&", - ("access_level", "=", "1"), - "|", - ("plan_id.user_ids", "in", [user.id]), - ("plan_id.server_ids.user_ids", "in", [user.id]) - ] - - - - - - - - - - Plan Line: Manager read - - - - - ["&", - ("access_level", "<=", "2"), - "|", - "|", ("plan_id.user_ids", "in", [user.id]), ("plan_id.manager_ids", "in", [user.id]), - "|", - ("plan_id.server_ids", "=", False), - "|", - ("plan_id.server_ids.user_ids", "in", [user.id]), - ("plan_id.server_ids.manager_ids", "in", [user.id]) - ] - - - - - - - - - - Plan Line: Manager write & create - - - - ["&", ("access_level", "<=", "2"), ("plan_id.manager_ids", "in", [user.id])] - - - - - - - - - - Plan Line: Manager unlink - - - - [ - ("access_level", "<=", "2"), - ("create_uid", "=", user.id), - ("plan_id.manager_ids", "in", [user.id]) - ] - - - - - - - - - - Plan Line: Root unrestricted access - - - [(1, '=', 1)] - - diff --git a/addons/cetmix_tower_server/security/cx_tower_plan_log_security.xml b/addons/cetmix_tower_server/security/cx_tower_plan_log_security.xml deleted file mode 100644 index 4354c17..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_plan_log_security.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - Tower plan log: user access rule - - - [ - ("access_level", "=", "1"), - ("create_uid", "=", user.id), - ("server_id.user_ids", "in", [user.id]) - ] - - - - - - - - Tower plan log: manager access rule - - - [ - "&", - ("access_level", "<=", "2"), - "|", - ("server_id.user_ids", "in", [user.id]), - ("server_id.manager_ids", "in", [user.id]) - ] - - - - - - Tower plan log: root access rule - - [(1, "=", 1)] - - - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_plan_security.xml b/addons/cetmix_tower_server/security/cx_tower_plan_security.xml deleted file mode 100644 index 22b4034..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_plan_security.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - Plan: User read - - - - ["&", - ("access_level", "=", "1"), - "|", - ("user_ids", "in", [user.id]), - ("server_ids.user_ids", "in", [user.id]) - ] - - - - - - - - - - Plan: Manager read - - - - - ["&", - ("access_level", "<=", "2"), - "|", - "|", ("user_ids", "in", [user.id]), ("manager_ids", "in", [user.id]), - "|", - ("server_ids", "=", False), - "|", - ("server_ids.user_ids", "in", [user.id]), - ("server_ids.manager_ids", "in", [user.id]) - ] - - - - - - - - - - Plan: Manager write & create - - - - ["&", ("access_level", "<=", "2"), ("manager_ids", "in", [user.id])] - - - - - - - - - - Plan: Manager unlink - - - - [ - ("access_level", "<=", "2"), - ("create_uid", "=", user.id), - ("manager_ids", "in", [user.id]) - ] - - - - - - - - - - Plan: Root unrestricted access - - - [(1, '=', 1)] - - diff --git a/addons/cetmix_tower_server/security/cx_tower_scheduled_task_cv_security.xml b/addons/cetmix_tower_server/security/cx_tower_scheduled_task_cv_security.xml deleted file mode 100644 index 8574d70..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_scheduled_task_cv_security.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - Scheduled Task CV: manager read access - - - - - - - - [ - '|', - ('scheduled_task_id.user_ids', 'in', [user.id]), - '|', - ('scheduled_task_id.server_ids.user_ids', 'in', [user.id]), - ('scheduled_task_id.server_ids.manager_ids', 'in', [user.id]) - ] - - - - - - Scheduled Task CV: manager write access - - - - - - - - [('scheduled_task_id.manager_ids', 'in', [user.id])] - - - - - - Scheduled Task CV: manager unlink access - - - - - - - - [ - ('scheduled_task_id.manager_ids', 'in', [user.id]), - ('create_uid', '=', user.id) - ] - - - - - - Scheduled Task CV: root full access - - - [(1, '=', 1)] - - diff --git a/addons/cetmix_tower_server/security/cx_tower_scheduled_task_security.xml b/addons/cetmix_tower_server/security/cx_tower_scheduled_task_security.xml deleted file mode 100644 index f0144a5..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_scheduled_task_security.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Scheduled Task: manager read access - - - - - - - - ['|', - '|', - ('user_ids', 'in', [user.id]), - ('manager_ids', 'in', [user.id]), - '|', - '|', - '|', - ('server_ids.user_ids', 'in', [user.id]), - ('server_ids.manager_ids', 'in', [user.id]), - ('server_template_ids.user_ids', 'in', [user.id]), - ('server_template_ids.manager_ids', 'in', [user.id]) - ] - - - - - - Scheduled Task: manager write access - - - - - - - - [('manager_ids', 'in', [user.id])] - - - - - - Scheduled Task: manager unlink access - - - - - - - - [ - ('manager_ids', 'in', [user.id]), - ('create_uid', '=', user.id) - ] - - - - - - Scheduled Task: root full access - - - - - - - [(1, '=', 1)] - - diff --git a/addons/cetmix_tower_server/security/cx_tower_server_log_security.xml b/addons/cetmix_tower_server/security/cx_tower_server_log_security.xml deleted file mode 100644 index 8982574..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_server_log_security.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - Tower server log: user access rule - - - [ - ("access_level", "=", "1"), - ("server_id.user_ids", "in", [user.id]) - ] - - - - - Tower server log: manager read access rule - - - [ - ("access_level", "<=", "2"), - "|", - ("server_id.user_ids", "in", [user.id]), - ("server_id.manager_ids", "in", [user.id]) - ] - - - - - - - - Tower server log: manager write access rule - - - [ - ("access_level", "<=", "2"), - ("server_id.manager_ids", "in", [user.id]) - ] - - - - - - - - Tower server log: manager unlink access rule - - - [ - ("access_level", "<=", "2"), - ("create_uid", "=", user.id), - ("server_id.manager_ids", "in", [user.id]) - ] - - - - - - - - - Tower server log: root access rule - - [(1, "=", 1)] - - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_server_security.xml b/addons/cetmix_tower_server/security/cx_tower_server_security.xml deleted file mode 100644 index e7c066f..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_server_security.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - Tower Server: user visibility rule - - - - [('user_ids', 'in', [user.id])] - - - - - - - - - - Tower Server: Manager Read (if follower or in manager_ids) - - - - - ['|', ('user_ids', 'in', [user.id]), - ('manager_ids', 'in', [user.id])] - - - - - - - - - - Tower Server: Manager Write & Create (if in manager_ids) - - - - [('manager_ids', 'in', [user.id])] - - - - - - - - - Tower Server: Manager Delete (if in manager_ids and creator) - - - - [('manager_ids', 'in', [user.id]), ('create_uid', '=', user.id)] - - - - - - - - - - Tower Server: root visibility rule - - [(1, '=', 1)] - - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_server_template_security.xml b/addons/cetmix_tower_server/security/cx_tower_server_template_security.xml deleted file mode 100644 index 97db784..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_server_template_security.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - Server Template: Manager Read Access - - ['|', ('user_ids', 'in', [user.id]), ('manager_ids', 'in', [user.id])] - - - - - - - - - - Server Template: Manager Write Access - - [('manager_ids', 'in', [user.id])] - - - - - - - - - - Server Template: Manager Delete Access - - [('manager_ids', 'in', [user.id]), ('create_uid','=', user.id)] - - - - - - - - - - Server Template: Root Full Access - - [(1,'=',1)] - - - - - - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_server_wizard_access_rules.xml b/addons/cetmix_tower_server/security/cx_tower_server_wizard_access_rules.xml deleted file mode 100644 index 3f8f429..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_server_wizard_access_rules.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - Creator only - - - [('create_uid', '=', user.id)] - - - - - Creator only - - - [('create_uid', '=', user.id)] - - - - - Creator only - - - [('create_uid', '=', user.id)] - - - - - Creator only - - - [('create_uid', '=', user.id)] - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_shortcut_security.xml b/addons/cetmix_tower_server/security/cx_tower_shortcut_security.xml deleted file mode 100644 index 935a747..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_shortcut_security.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - Tower Shortcut: user visibility rule - - - [ - ('server_ids.user_ids', 'in', [user.id]), - ('access_level', '=', '1') - ] - - - - - - Tower shortcut: manager visibility rule - - - [ - ('access_level', '<=', '2'), - '|', '|', '|', - ('server_ids.user_ids', 'in', [user.id]), - ('server_ids.manager_ids', 'in', [user.id]), - ('server_template_ids.user_ids', 'in', [user.id]), - ('server_template_ids.manager_ids', 'in', [user.id]), - ] - - - - - Tower shortcut: root visibility rule - - [(1, '=', 1)] - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_tag_security.xml b/addons/cetmix_tower_server/security/cx_tower_tag_security.xml deleted file mode 100644 index d7c4a35..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_tag_security.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - Tower Tag: User can read any record - - [(1, '=', 1)] - - - - - - - - - Tower Tag: Manager can create/edit/delete own records - - [('create_uid', '=', user.id)] - - - - - - - - - Tower Tag: Root has full access - - [(1, '=', 1)] - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_variable_option_security.xml b/addons/cetmix_tower_server/security/cx_tower_variable_option_security.xml deleted file mode 100644 index c961071..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_variable_option_security.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - Variable Option: User Read Access - - [('access_level', '=', '1')] - - - - - - - - - - Variable Option: Manager Read Access - - [('access_level', '<=', '2')] - - - - - - - - - - Variable Option: Manager Write/Create/Unlink Access - - [('access_level', '<=', '2'), ('create_uid', '=', user.id)] - - - - - - - - - - Variable Option: Root Full Access - - [(1, '=', 1)] - - - - - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_variable_security.xml b/addons/cetmix_tower_server/security/cx_tower_variable_security.xml deleted file mode 100644 index caf584a..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_variable_security.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - Variable: User Read Access - - [('access_level', '=', '1')] - - - - - - - - - - Variable: Manager Read Access - - [('access_level', '<=', '2')] - - - - - - - - - - Variable: Manager Write/Create/Unlink Access - - [('access_level', '<=', '2'), ('create_uid', '=', user.id)] - - - - - - - - - - Variable: Root Full Access - - [(1, '=', 1)] - - - - - - - diff --git a/addons/cetmix_tower_server/security/cx_tower_variable_value_security.xml b/addons/cetmix_tower_server/security/cx_tower_variable_value_security.xml deleted file mode 100644 index 511f010..0000000 --- a/addons/cetmix_tower_server/security/cx_tower_variable_value_security.xml +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - Variable Value: User Read Global Access - - [('access_level', '=', '1'), ('is_global', '=', True)] - - - - - - - - - Variable Value: User Read Server Access - - [('access_level', '=', '1'), ('server_id.user_ids', 'in', [user.id])] - - - - - - - - - Variable Value: User Read Plan Access - - [('access_level', '=', '1'), ('plan_line_action_id.plan_id.user_ids', 'in', [user.id])] - - - - - - - - - Variable Value: User Read Plan Server Access - - [('access_level', '=', '1'), ('plan_line_action_id.plan_id.server_ids.user_ids', 'in', [user.id])] - - - - - - - - - - Variable Value: Manager Read Global Access - - [('access_level', '<=', '2'), ('is_global', '=', True)] - - - - - - - - - Variable Value: Manager Read Server Access - - [('access_level', '<=', '2'), '|', ('server_id.user_ids', 'in', [user.id]), ('server_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Read Server Template Access - - [('access_level', '<=', '2'), '|', ('server_template_id.user_ids', 'in', [user.id]), ('server_template_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Read Plan Access - - [('access_level', '<=', '2'), '|', ('plan_line_action_id.plan_id.user_ids', 'in', [user.id]), ('plan_line_action_id.plan_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Read Plan Server Access - - [('access_level', '<=', '2'), '|', ('plan_line_action_id.plan_id.server_ids.user_ids', 'in', [user.id]), ('plan_line_action_id.plan_id.server_ids.manager_ids', 'in', [user.id])] - - - - - - - - - - Variable Value: Manager Write/Create Server Access - - [('access_level', '<=', '2'), ('server_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Write/Create Server Template Access - - [('access_level', '<=', '2'), ('server_template_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Write/Create Plan Access - - [('access_level', '<=', '2'), ('plan_line_action_id.plan_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Write/Create Plan Server Access - - [('access_level', '<=', '2'), ('plan_line_action_id.plan_id.server_ids.manager_ids', 'in', [user.id])] - - - - - - - - - - Variable Value: Manager Unlink Server Access - - [('access_level', '<=', '2'), ('create_uid', '=', user.id), ('server_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Unlink Server Template Access - - [('access_level', '<=', '2'), ('create_uid', '=', user.id), ('server_template_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Unlink Plan Access - - [('access_level', '<=', '2'), ('create_uid', '=', user.id), ('plan_line_action_id.plan_id.manager_ids', 'in', [user.id])] - - - - - - - - - Variable Value: Manager Unlink Plan Server Access - - [('access_level', '<=', '2'), ('create_uid', '=', user.id), ('plan_line_action_id.plan_id.server_ids.manager_ids', 'in', [user.id])] - - - - - - - - - - Variable Value: Root Full Access - - [(1, '=', 1)] - - - - - - - - diff --git a/addons/cetmix_tower_server/security/ir.model.access.csv b/addons/cetmix_tower_server/security/ir.model.access.csv deleted file mode 100644 index 1cb8543..0000000 --- a/addons/cetmix_tower_server/security/ir.model.access.csv +++ /dev/null @@ -1,70 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_variable_user,Variable->User,model_cx_tower_variable,group_user,1,0,0,0 -access_variable_manager,Variable->Manager,model_cx_tower_variable,group_manager,1,1,1,0 -access_variable_root,Variable->Root,model_cx_tower_variable,group_root,1,1,1,1 -access_variable_value_user,Variable Value->User,model_cx_tower_variable_value,group_user,1,0,0,0 -access_variable_value_manager,Variable Value->Manager,model_cx_tower_variable_value,group_manager,1,1,1,1 -access_variable_value_root,Variable Value->Root,model_cx_tower_variable_value,group_root,1,1,1,1 -access_os_user,OS->User,model_cx_tower_os,group_user,1,0,0,0 -access_os_root,OS->Root,model_cx_tower_os,group_root,1,1,1,1 -access_tag_user,Tag->User,model_cx_tower_tag,group_user,1,0,0,0 -access_tag_manager,Tag->Manager,model_cx_tower_tag,group_manager,1,1,1,1 -access_tag_root,Tag->Root,model_cx_tower_tag,group_root,1,1,1,1 -access_server_user,Server->User,model_cx_tower_server,group_user,1,0,0,0 -access_server_manager,Server->Manager,model_cx_tower_server,group_manager,1,1,1,1 -access_server_root,Server->Root,model_cx_tower_server,group_root,1,1,1,1 -access_command_user,Command->User,model_cx_tower_command,group_user,1,0,0,0 -access_command_manager,Command->Manager,model_cx_tower_command,group_manager,1,1,1,1 -access_command_root,Command->Root,model_cx_tower_command,group_root,1,1,1,1 -access_run_command_user,Run Command->User,model_cx_tower_command_run_wizard,group_user,1,1,1,1 -access_run_command_variable_value_user,Run Command Variable Value->User,model_cx_tower_command_run_wizard_variable_value,group_user,1,1,1,1 -access_execute_plan_user,Run Plan->User,model_cx_tower_plan_run_wizard,group_user,1,1,1,1 -access_execute_plan_variable_value_user,Run Plan Variable Value->User,model_cx_tower_plan_run_wizard_variable_value,group_user,1,1,1,1 -access_key_user,Key->User,model_cx_tower_key,group_user,0,0,0,0 -access_key_manager,Key->Manager,model_cx_tower_key,group_manager,1,1,1,1 -access_key_root,Key->Root,model_cx_tower_key,group_root,1,1,1,1 -access_key_value_manager,Key Value->Manager,model_cx_tower_key_value,group_manager,1,1,1,1 -access_key_value_root,Key Value->Root,model_cx_tower_key_value,group_root,1,1,1,1 -access_command_log_user,Command Log->User,model_cx_tower_command_log,group_user,1,0,0,0 -access_command_log_manager,Command Log->Manager,model_cx_tower_command_log,group_manager,1,0,0,0 -access_command_log_root,Command Log->Root,model_cx_tower_command_log,group_root,1,0,0,0 -access_plan_user,Plan->User,model_cx_tower_plan,group_user,1,0,0,0 -access_plan_manager,Plan->Manager,model_cx_tower_plan,group_manager,1,1,1,1 -access_plan_root,Plan->Root,model_cx_tower_plan,group_root,1,1,1,1 -access_plan_line_user,Plan Line->User,model_cx_tower_plan_line,group_user,1,0,0,0 -access_plan_line_manager,Plan Line->Manager,model_cx_tower_plan_line,group_manager,1,1,1,1 -access_plan_line_root,Plan Line->Root,model_cx_tower_plan_line,group_root,1,1,1,1 -access_plan_line_action_user,Plan Line Action->User,model_cx_tower_plan_line_action,group_user,1,0,0,0 -access_plan_line_action_manager,Plan Line Action->Manager,model_cx_tower_plan_line_action,group_manager,1,1,1,1 -access_plan_line_action_root,Plan Line Action->Root,model_cx_tower_plan_line_action,group_root,1,1,1,1 -access_plan_log_user,Plan Log->User,model_cx_tower_plan_log,group_user,1,0,0,0 -access_plan_log_manager,Plan Log->Manager,model_cx_tower_plan_log,group_manager,1,0,0,0 -access_plan_log_root,Plan Log->Root,model_cx_tower_plan_log,group_root,1,0,0,0 -access_file_user,File->User,model_cx_tower_file,group_user,1,0,0,0 -access_file_manager,File->Manager,model_cx_tower_file,group_manager,1,1,1,1 -access_file_root,File->Root,model_cx_tower_file,group_root,1,1,1,1 -access_file_template_manager,File Template->Manager,model_cx_tower_file_template,group_manager,1,1,1,1 -access_file_template_root,File Template->Root,model_cx_tower_file_template,group_root,1,1,1,1 -access_server_log_user,Server Log->User,model_cx_tower_server_log,group_user,1,0,0,0 -access_server_log_manager,Server Log->Manager,model_cx_tower_server_log,group_manager,1,1,1,1 -access_server_log_root,Server Log->Root,model_cx_tower_server_log,group_root,1,1,1,1 -access_server_template_manager,Server Template->Manager,model_cx_tower_server_template,group_manager,1,1,1,1 -access_server_template_root,Server Template->Root,model_cx_tower_server_template,group_root,1,1,1,1 -access_create_server_from_template_manager,Create Server From Template->Manager,model_cx_tower_server_template_create_wizard,group_manager,1,1,1,1 -access_create_server_from_template_line_manager,Create Server From Template Line->Manager,model_cx_tower_server_template_create_wizard_line,group_manager,1,1,1,1 -access_cx_tower_variable_option_user,Variable Option->User,model_cx_tower_variable_option,group_user,1,0,0,0 -access_cx_tower_variable_option_manager,Variable Option->Manager,model_cx_tower_variable_option,group_manager,1,1,1,1 -access_cx_tower_variable_option_root,Variable Option->Root,model_cx_tower_variable_option,group_root,1,1,1,1 -access_cx_tower_vault_no_access,cx.tower.vault no access,model_cx_tower_vault,group_user,0,0,0,0 -access_cx_tower_server_host_key_wizard_manager,Show Host Key->Manager,model_cx_tower_server_host_key_wizard,group_manager,1,1,1,1 -access_cx_tower_server_host_key_wizard_root,Show Host Key->Root,model_cx_tower_server_host_key_wizard,group_root,1,1,1,1 -access_cetmix_tower_user,Cetmix Tower->User,model_cetmix_tower,group_user,1,1,0,0 -access_shortcut_user,Shortcut->User,model_cx_tower_shortcut,group_user,1,0,0,0 -access_shortcut_manager,Shortcut->Manager,model_cx_tower_shortcut,group_manager,1,0,0,0 -access_shortcut_root,Shortcut->Root,model_cx_tower_shortcut,group_root,1,1,1,1 -access_scheduled_task_user,Scheduled Task->User,model_cx_tower_scheduled_task,group_user,0,0,0,0 -access_scheduled_task_manager,Scheduled Task->Manager,model_cx_tower_scheduled_task,group_manager,1,1,1,1 -access_scheduled_task_root,Scheduled Task->Root,model_cx_tower_scheduled_task,group_root,1,1,1,1 -access_scheduled_task_cv_user,Scheduled Task Custom Variable Value->User,model_cx_tower_scheduled_task_cv,group_user,0,0,0,0 -access_scheduled_task_cv_manager,Scheduled Task Custom Variable Value->Manager,model_cx_tower_scheduled_task_cv,group_manager,1,1,1,1 -access_scheduled_task_cv_root,Scheduled Task Custom Variable Value->Root,model_cx_tower_scheduled_task_cv,group_root,1,1,1,1 diff --git a/addons/cetmix_tower_server/ssh/__init__.py b/addons/cetmix_tower_server/ssh/__init__.py deleted file mode 100644 index c601950..0000000 --- a/addons/cetmix_tower_server/ssh/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import ssh diff --git a/addons/cetmix_tower_server/ssh/ssh.py b/addons/cetmix_tower_server/ssh/ssh.py deleted file mode 100644 index 641fee0..0000000 --- a/addons/cetmix_tower_server/ssh/ssh.py +++ /dev/null @@ -1,382 +0,0 @@ -# ruff: noqa: UP007 - -import io -import logging -import time -from typing import Optional, Union - -_logger = logging.getLogger(__name__) - - -try: - from paramiko import ( - AutoAddPolicy, - DSSKey, - ECDSAKey, - Ed25519Key, - MissingHostKeyPolicy, - RSAKey, - SFTPClient, - SSHClient, - SSHException, - ) -except ImportError: - _logger.error( - "Looks like 'paramiko' is not installed, please try to " - "install it using 'pip install paramiko'" - ) - AutoAddPolicy = MissingHostKeyPolicy = RSAKey = SSHClient = None - - -class KeyLoader: - """ - Utility for loading private SSH key in supported formats. - """ - - @staticmethod - def load_private_key(ssh_key: str) -> Union[RSAKey, DSSKey, ECDSAKey, Ed25519Key]: - """ - Load a private SSH key from a string. - """ - key_file = io.StringIO(ssh_key) - for key_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key): - try: - key_file.seek(0) - return key_class.from_private_key(key_file) - except SSHException: - _logger.warning( - f"KeyLoader: failed to load key through {key_class.__name__}." - ) - _logger.error( - "KeyLoader: unable to load private key. " - "Unsupported format or invalid SSH key." - ) - raise ValueError("Unsupported format or invalid SSH key.") - - -class SSHConnection: - """ - Class for managing SSH connection. - """ - - def __init__( - self, - host: str, - port: int, - username: str, - password: Optional[str] = None, - ssh_key: Optional[str] = None, - host_key: Optional[str] = None, - mode: str = "p", # "p" for password, "k" for key - allow_agent: bool = False, - timeout: int = 5000, - ): - """ - Initialize the SSHConnection instance. - """ - self.host = host - self.port = port - self.username = username - self.password = password - self.ssh_key = ssh_key - self.host_key = host_key - self.mode = mode - self.allow_agent = allow_agent - self.timeout = timeout - self._ssh_client: Optional[SSHClient] = None - - def connect(self) -> SSHClient: - """ - Connect to the SSH server. - """ - if self._ssh_client is not None: - return self._ssh_client - - self._ssh_client = SSHClient() - self._ssh_client.load_system_host_keys() - - if self.host_key: - self._ssh_client.set_missing_host_key_policy( - CustomHostKeyPolicy(self.host_key) - ) - else: - self._ssh_client.set_missing_host_key_policy(AutoAddPolicy()) - - connect_params = { - "hostname": self.host, - "port": self.port, - "username": self.username, - "allow_agent": self.allow_agent, - "timeout": self.timeout, - } - - if self.mode == "p": - if not self.password: - raise ValueError("For password mode, you need to pass a password.") - connect_params["password"] = self.password - elif self.mode == "k": - if not self.ssh_key: - raise ValueError("For key mode, you need to pass an SSH key.") - connect_params["pkey"] = KeyLoader.load_private_key(self.ssh_key) - else: - raise ValueError(f"Unsupported connection mode: {self.mode}") - - self._ssh_client.connect(**connect_params) - return self._ssh_client - - def disconnect(self) -> None: - """ - Disconnect the SSH connection. - """ - if self._ssh_client: - _logger.info("SSHConnection: closing SSH connection.") - self._ssh_client.close() - self._ssh_client = None - - def get_transport(self): - """ - Get the SSH transport. - """ - if self._ssh_client is None: - self.connect() - return self._ssh_client.get_transport() - - -class CustomHostKeyPolicy(MissingHostKeyPolicy): - """ - Custom SSH host key policy for validating the server's host key. - - This policy compares the server's host key (in Base64 format) with the expected key. - If they do not match, an SSHException is raised to prevent connecting - to an untrusted server. If they match, the key is added to the client's host keys. - """ - - def __init__(self, expected_host_key: str): - """ - Initialize the policy with the expected host key. - - Args: - expected_host_key (str): The expected host key in Base64 format. - """ - self.expected_host_key = expected_host_key - - def missing_host_key(self, client, hostname, key): - """ - Called when the SSH client receives a host key from the server - that is not in its known hosts. - - Args: - client: The SSH client instance. - hostname: The hostname of the server. - key: The host key received from the server. - - Raises: - SSHException: If the received host key does not match the expected host key. - """ - received_key = key.get_base64() - if received_key != self.expected_host_key: - raise SSHException(f"Host key mismatch for {hostname}. ") - # If the key matches, add it to the client's known hosts - client._host_keys.add(hostname, key.get_name(), key) - - -class SftpService: - """ - Service for working with SFTP, using SSH connection. - """ - - def __init__(self, connection: SSHConnection): - """ - Initialize the SftpService instance. - """ - self.connection = connection - self._sftp_client: Optional[SFTPClient] = None - - def get_client(self) -> SFTPClient: - """ - Get the SFTP client. - """ - if self._sftp_client is None: - transport = self.connection.get_transport() - self._sftp_client = SFTPClient.from_transport(transport) - return self._sftp_client - - def upload_file(self, file: Union[str, io.BytesIO], remote_path: str) -> None: - """ - Upload a file to the remote server. - """ - client = self.get_client() - if isinstance(file, io.BytesIO): - client.putfo(file, remote_path) - elif isinstance(file, str): - client.put(file, remote_path) - else: - raise TypeError(f"File type {type(file).__name__} is not supported.") - - def download_file(self, remote_path: str) -> bytes: - """ - Download a file from the remote server. - """ - client = self.get_client() - with client.open(remote_path, "rb") as remote_file: - return remote_file.read() - - def delete_file(self, remote_path: str) -> None: - """ - Delete a file from the remote server. - """ - client = self.get_client() - client.remove(remote_path) - - def disconnect(self) -> None: - """ - Disconnect the SFTP client. - """ - if self._sftp_client: - _logger.info("SftpService: closing SFTP connection.") - self._sftp_client.close() - self._sftp_client = None - - -class CommandExecutor: - """ - Class for executing commands on a remote server. - """ - - def __init__(self, connection: SSHConnection): - """ - Initialize the CommandExecutor instance. - """ - self.connection = connection - - def exec_command( - self, command: str, sudo: Optional[str] = None - ) -> tuple[int, list[str], list[str]]: - """ - Run a command on the remote server. - - Args: - command (str): The command to execute. - sudo (Optional[str]): Sudo mode. - - Returns: - tuple: - - exit_status (int) - - stdout (list[str]) - - stderr (list[str]) - """ - ssh_client = self.connection.connect() - use_sudo_with_password = sudo == "p" and self.connection.username != "root" - - if use_sudo_with_password and not self.connection.password: - return 255, [], ["Sudo password not provided!"] - - try: - stdin, stdout, stderr = ssh_client.exec_command(command) - if use_sudo_with_password: - stdin.write(self.connection.password + "\n") - stdin.flush() - exit_status = stdout.channel.recv_exit_status() - response = stdout.readlines() - error = stderr.readlines() - return exit_status, response, error - except Exception as e: - return 255, [], [str(e)] - - -class SSHManager: - """ - Facade for working with SSH connection, SFTP and command execution. - """ - - _connection_cache = {} - - def __new__(cls, connection: SSHConnection): - """ - Create a new SSHManager instance. - """ - key = ( - connection.host, - connection.port, - connection.username, - connection.mode, - connection.allow_agent, - connection.password or "", - connection.ssh_key or "", - connection.host_key or "", - ) - if key in cls._connection_cache: - instance, created_at, cached_timeout = cls._connection_cache[key] - # if timeout is changed, update the cached timeout - if connection.timeout != cached_timeout: - cls.delete_cache(key) - else: - _logger.info( - "Using cached SSH connection for " - "host=%s, port=%s, user=%s, mode=%s", - connection.host, - connection.port, - connection.username, - connection.mode, - ) - return instance - - _logger.info( - "Creating new SSH connection for host=%s, port=%s, user=%s, mode=%s", - connection.host, - connection.port, - connection.username, - connection.mode, - ) - instance = super().__new__(cls) - cls._connection_cache[key] = (instance, time.time(), connection.timeout) - return instance - - def __init__(self, connection: SSHConnection): - """ - Initialize the SSHManager instance. - """ - # initialize only once - if hasattr(self, "_initialized") and self._initialized: - return - self.connection = connection - self.command_executor = CommandExecutor(connection) - self.sftp_service = SftpService(connection) - self._initialized = True - - @classmethod - def delete_cache(cls, key): - """ - Delete the cache of SSH connections. - """ - if key in SSHManager._connection_cache: - del SSHManager._connection_cache[key] - - def disconnect(self) -> None: - """ - Disconnect the SSH connection and SFTP client. - """ - if self.sftp_service._sftp_client is not None: - self.sftp_service.disconnect() - - if self.connection._ssh_client is not None: - self.connection.disconnect() - - key = ( - self.connection.host, - self.connection.port, - self.connection.username, - self.connection.mode, - self.connection.allow_agent, - self.connection.password or "", - self.connection.ssh_key or "", - self.connection.host_key or "", - ) - self.delete_cache(key) - - @classmethod - def get_connection_cache(cls): - """ - Get the connection cache. - """ - return cls._connection_cache diff --git a/addons/cetmix_tower_server/static/description/banner.png b/addons/cetmix_tower_server/static/description/banner.png deleted file mode 100644 index 98c44a7..0000000 Binary files a/addons/cetmix_tower_server/static/description/banner.png and /dev/null differ diff --git a/addons/cetmix_tower_server/static/description/icon.png b/addons/cetmix_tower_server/static/description/icon.png deleted file mode 100644 index 2507f55..0000000 Binary files a/addons/cetmix_tower_server/static/description/icon.png and /dev/null differ diff --git a/addons/cetmix_tower_server/static/description/images/server_from_template_auto_action.png b/addons/cetmix_tower_server/static/description/images/server_from_template_auto_action.png deleted file mode 100644 index c1a7061..0000000 Binary files a/addons/cetmix_tower_server/static/description/images/server_from_template_auto_action.png and /dev/null differ diff --git a/addons/cetmix_tower_server/static/description/images/server_log_tab.png b/addons/cetmix_tower_server/static/description/images/server_log_tab.png deleted file mode 100644 index 01001d1..0000000 Binary files a/addons/cetmix_tower_server/static/description/images/server_log_tab.png and /dev/null differ diff --git a/addons/cetmix_tower_server/static/description/images/server_log_usage_1.png b/addons/cetmix_tower_server/static/description/images/server_log_usage_1.png deleted file mode 100644 index 96c7d33..0000000 Binary files a/addons/cetmix_tower_server/static/description/images/server_log_usage_1.png and /dev/null differ diff --git a/addons/cetmix_tower_server/static/description/images/server_log_usage_2.png b/addons/cetmix_tower_server/static/description/images/server_log_usage_2.png deleted file mode 100644 index 9fed0cd..0000000 Binary files a/addons/cetmix_tower_server/static/description/images/server_log_usage_2.png and /dev/null differ diff --git a/addons/cetmix_tower_server/static/description/images/user_profile.png b/addons/cetmix_tower_server/static/description/images/user_profile.png deleted file mode 100644 index 5a7db2f..0000000 Binary files a/addons/cetmix_tower_server/static/description/images/user_profile.png and /dev/null differ diff --git a/addons/cetmix_tower_server/static/description/index.html b/addons/cetmix_tower_server/static/description/index.html deleted file mode 100644 index 7be7f3f..0000000 --- a/addons/cetmix_tower_server/static/description/index.html +++ /dev/null @@ -1,747 +0,0 @@ - - - - - -Cetmix Tower Server - - - -
    -

    Cetmix Tower Server

    - - -

    Beta License: AGPL-3 cetmix/cetmix-tower

    -

    Cetmix Tower offers a streamlined -solution for managing remote servers and applications via SSH or API -calls directly from Odoo. It is designed for -versatility across different operating systems and software -environments, providing a practical option for those looking to manage -servers without getting tied down by vendor or technology constraints.

    -

    Please refer to the official -documentation for detailed information.

    -

    Table of contents

    - -
    -

    Configuration

    -

    Please refer to the official -documentation for detailed configuration -instructions.

    -
    -
    -

    Usage

    -

    Please refer to the official -documentation for detailed usage -instructions.

    -
    -
    -

    Changelog

    -
    -

    16.0.2.2.8 (2025-12-22)

    -
      -
    • Bugfixes: Handle malformed expressions in flight plan line conditions. -(5154)
    • -
    -
    -
    -

    16.0.2.2.7 (2025-12-16)

    -
      -
    • Features: Support for ANSI formatting in server logs. (5141)
    • -
    • Bugfixes: UI/UX fixed and improvements. (5141)
    • -
    -
    -
    -

    16.0.2.2.6 (2025-12-11)

    -
      -
    • Features: Improve search views, implement the search panel for -selected views. (5139)
    • -
    -
    -
    -

    16.0.2.2.5 (2025-12-10)

    -
      -
    • Bugfixes: Custom values in flight plan are lost in a skipped command -and are not available after it. (5129)
    • -
    -
    -
    -

    16.0.2.2.4 (2025-12-10)

    -
      -
    • Features: Parse empty or missing key values as ‘None’ instead of -leaving key reference as is. (5134)
    • -
    -
    -
    -

    16.0.2.2.3 (2025-12-03)

    -
      -
    • Bugfixes: Save correct error message in log when SSH connection fails. -(5109)
    • -
    -
    -
    -

    16.0.2.2.2 (2025-12-03)

    -
      -
    • Bugfixes: Make variables selectable in scheduled tasks (5105)
    • -
    -
    -
    -

    16.0.2.2.0 (2025-11-12)

    -
      -
    • Features: Integrate user notifications into the main module, drop the -‘cetmix_tower_notify_backend’ module. (5074)
    • -
    -
    -
    -

    16.0.2.0.6 (2025-10-27)

    -
      -
    • Features: Tag mixin and helper commands. (5039)
    • -
    -
    -
    -

    16.0.2.0.5 (2025-10-16)

    -
      -
    • Bugfixes: Flight plan command exception handling (4930)
    • -
    -
    -
    -

    16.0.2.0.4 (2025-10-13)

    -
      -
    • Features: Auto update references for related records (5005)
    • -
    -
    -
    -

    16.0.2.0.3 (2025-10-13)

    -
      -
    • Features: Terminate running flight plan manually (3410)
    • -
    -
    -
    -

    16.0.2.0.2 (2025-10-08)

    -
      -
    • Features: UI/UX improvements (4996)
    • -
    • Bugfixes: Handle secret values when a record is duplicated using -copy() (4996)
    • -
    -
    -
    -

    16.0.2.0.1 (2025-10-08)

    -
      -
    • Bugfixes: Improve variable value references uniqueness (4961)
    • -
    -
    -
    -

    16.0.2.0.0 (2025-10-07)

    -
      -
    • Features: ‘Cetmix Tower Vault’ - new way of centralized password/key -management (4824)
    • -
    -
    -
    -

    16.0.1.7.2 (2025-09-18)

    -
      -
    • Features: Set ‘Auto Sync’ in files from file templates (4949)
    • -
    -
    -
    -

    16.0.1.7.1 (2025-09-10)

    -
      -
    • Bugfixes: Check custom values in flight plan line condition (4922)
    • -
    -
    -
    -

    16.0.1.6.4 (2025-08-18)

    -
      -
    • Features: Improve the extendability of the file upload command. (4759)
    • -
    -
    -
    -

    16.0.1.6.3 (2025-08-13)

    -
      -
    • Features: Improve access settings for logs (4866)
    • -
    -
    -
    -

    16.0.1.6.2 (2025-08-05)

    -
      -
    • Bugfixes: Pin paramiko version to “<4” to maintain compatibility with -legacy installations (4891)
    • -
    -
    -
    -

    16.0.1.6.0 (2025-07-30)

    -
      -
    • Features: Optional behaviour when file uploaded by command already -exists on the server. (4740)
    • -
    -
    -
    -

    16.0.1.5.3 (2025-07-29)

    -
      -
    • Features: Make file references server dependent to be more unique -(4870)
    • -
    -
    -
    -

    16.0.1.5.1 (2025-07-25)

    -
      -
    • Features: Select secrets from dropdown list in the code fields (4853)
    • -
    -
    -
    -

    16.0.1.5.0 (2025-07-22)

    -
      -
    • Features: Select variables from dropdown list in the code fields -(4827)
    • -
    -
    -
    -

    16.0.1.3.0 (2025-07-17)

    -
      -
    • Features: Add the tldextract and dnspython libraries. (4737)
    • -
    -
    -
    -

    16.0.1.1.4 (2025-07-07)

    -
      -
    • Bugfixes: Command log sorting (4816)
    • -
    -
    -
    -

    16.0.1.1.2 (2025-06-25)

    -
      -
    • Features: Required variables in servers (4779)
    • -
    -
    -
    -

    16.0.1.1.1 (2025-06-21)

    -
      -
    • Features: Command view improvements (4753)
    • -
    -
    -
    -

    16.0.1.1.0 (2025-06-20)

    -
      -
    • Features: Run commands and flight plans using scheduled tasks. (4650)
    • -
    -
    -
    -

    16.0.1.0.12 (2025-06-06)

    -
      -
    • Features: Improve command and flight plan log management. (4749)
    • -
    -
    -
    -

    16.0.1.0.11 (2025-06-06)

    -
      -
    • Bugfixes: Host key cannot be retrieved from the UI. (4747)
    • -
    -
    -
    -

    16.0.1.0.10 (2025-05-24)

    -
      -
    • Features: Improve command log and flight plan form views (4697)
    • -
    -
    -
    -

    16.0.1.0.9 (2025-05-23)

    -
      -
    • Bugfixes: Error when rendering a file not attached to a server. (4715)
    • -
    -
    -
    -

    16.0.1.0.8 (2025-05-21)

    -
      -
    • Features: References for secret values. (4696)
    • -
    • Features: Make the “Host key” field non-required in the form view to -improve the UX. (4699)
    • -
    -
    -
    -

    16.0.1.0.7 (2025-05-16)

    -
      -
    • Features: Option to preserve command splitting when using sudo​. (4641)
    • -
    • Features: Record references for files. (4670)
    • -
    • Features: Use sudo parameter to pass sudo mode to command runner -instead of using context. (4678)
    • -
    • Bugfixes: Incorrect sudo usage in commands run in wizard. Pass ‘No -split for sudo’ property to commands run in wizard. (4679)
    • -
    -
    -
    -

    16.0.1.0.6 (2025-05-16)

    -
      -
    • Features: Improve the key storage functionality. (4686)
    • -
    -
    -
    -

    16.0.1.0.5 (2025-05-09)

    -
      -
    • Bugfixes: Non-critical issues and performance improvements. (4663)
    • -
    -
    -
    -

    16.0.1.0.4 (2025-04-30)

    -
      -
    • Features: UI/UX improvements. (4642)
    • -
    -
    -
    -

    16.0.1.0.3 (2025-04-22)

    -
      -
    • Features: Allow to pass custom variable values to commands (4524)
    • -
    • Features: Cetmix Tower Odoo Automation model: pass custom variable -values to the server_run_command method. (4547)
    • -
    • Bugfixes: Random id generation, sudo command parsing, record rule -names, spelling errors in descriptions. (4612)
    • -
    -
    -
    -

    16.0.1.0.2 (2025-04-22)

    -
      -
    • Bugfixes: Refactor secret value handling, fix the new server template -creation wizard. (4601)
    • -
    -
    -
    -

    16.0.1.0.1

    -

    Release for Odoo 16.0

    -
    -
    -
    -

    Bug Tracker

    -

    Bugs are tracked on GitHub Issues. -In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

    -

    Do not contact contributors directly about support or help with technical issues.

    -
    -
    -

    Credits

    -
    -

    Authors

    -
      -
    • Cetmix
    • -
    -
    -
    -

    Maintainers

    -

    This module is part of the cetmix/cetmix-tower project on GitHub.

    -

    You are welcome to contribute.

    -
    -
    -
    - - diff --git a/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.esm.js b/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.esm.js deleted file mode 100644 index 94127e5..0000000 --- a/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.esm.js +++ /dev/null @@ -1,507 +0,0 @@ -/** @odoo-module **/ - -import {AceField} from "@web/views/fields/ace/ace_field"; -import {AutocompletePopup} from "./autocomplete_popup.esm"; -import {registry} from "@web/core/registry"; -import {useService} from "@web/core/utils/hooks"; -import {useState} from "@odoo/owl"; - -const POPUP_FALLBACK_WIDTH = 500; -const POPUP_FALLBACK_HEIGHT = 300; - -class AceCommandField extends AceField { - /** - * Initialize the component with required services and properties - */ - setup() { - super.setup(); - this.orm = useService("orm"); - this.inputListener = null; - this.clickOutsideListener = null; - this.inputTimeout = null; - this.variables = []; - this.secrets = []; - - // Use reactive state for properties that affect rendering - this.state = useState({ - showPopup: false, - popupItems: [], - popupPosition: {}, - selectedIndex: 0, - // Add popup type to distinguish between variables and secrets - popupType: "variables", - }); - - this.updateSelectedIndex = this.updateSelectedIndex.bind(this); - } - - /** - * Load variables from the backend using ORM service - * @returns {Promise} - */ - async loadVariables() { - try { - this.variables = await this.orm.searchRead( - "cx.tower.variable", - [], - ["name", "reference"] - ); - console.log(`Loaded ${this.variables.length} variables for autocomplete`); - } catch (error) { - console.error("Failed to load variables for autocomplete:", error); - this.variables = []; - this.env.services.notification.add( - "Failed to load autocomplete variables", - {type: "warning"} - ); - } - } - - /** - * Load secrets from the backend using ORM service - * @returns {Promise} - */ - async loadSecrets() { - try { - this.secrets = await this.orm.searchRead( - "cx.tower.key", - [["key_type", "=", "s"]], - ["name", "reference"] - ); - console.log(`Loaded ${this.secrets.length} secrets for autocomplete`); - } catch (error) { - console.error("Failed to load secrets for autocomplete:", error); - this.secrets = []; - this.env.services.notification.add("Failed to load autocomplete secrets", { - type: "warning", - }); - } - } - - /** - * Set up ACE editor with custom autocompletion - */ - setupAce() { - super.setupAce(); - - if (this.aceEditor) { - this.setupCustomAutocompletion(); - } - } - - /** - * Configure custom autocompletion commands and keyboard bindings for ACE editor - */ - setupCustomAutocompletion() { - // Remove any existing conflicting commands first - this.aceEditor.commands.removeCommand("startAutocomplete"); - this.aceEditor.commands.removeCommand("expandSnippet"); - - // Only add the main autocomplete trigger command - this.aceEditor.commands.addCommand({ - name: "customAutoComplete", - bindKey: {win: "Ctrl-Space", mac: null}, - exec: (editor) => { - this.showCustomCompletions(editor); - return true; - }, - }); - - // Set up input listener for {{ and #! triggers - this.inputListener = () => { - // Clear any existing timeout - if (this.inputTimeout) { - clearTimeout(this.inputTimeout); - } - // Use setTimeout to ensure the text is fully processed - this.inputTimeout = setTimeout(() => { - const cursor = this.aceEditor.getCursorPosition(); - const session = this.aceEditor.getSession(); - const line = session.getLine(cursor.row); - const textBeforeCursor = line.substring(0, cursor.column); - - // Check for variables trigger {{ - if (textBeforeCursor.endsWith("{{")) { - // Remove {{ symbols from editor - const startColumn = Math.max(0, cursor.column - 2); - const range = { - start: {row: cursor.row, column: startColumn}, - end: {row: cursor.row, column: cursor.column}, - }; - session.replace(range, ""); - - // Update cursor position - const newCursor = { - row: cursor.row, - column: startColumn, - }; - this.aceEditor.moveCursorToPosition(newCursor); - this.showCustomCompletions(this.aceEditor, "variables"); - } - // Check for secrets trigger !# - else if (textBeforeCursor.endsWith("#!")) { - // Remove !# symbols from editor - const startColumn = Math.max(0, cursor.column - 2); - const range = { - start: {row: cursor.row, column: startColumn}, - end: {row: cursor.row, column: cursor.column}, - }; - session.replace(range, ""); - - // Update cursor position - const newCursor = { - row: cursor.row, - column: startColumn, - }; - this.aceEditor.moveCursorToPosition(newCursor); - this.showCustomCompletions(this.aceEditor, "secrets"); - } - }, 10); - }; - - this.aceEditor.on("input", this.inputListener); - } - - /** - * Show custom completions popup with available variables or secrets - * @param {Object} editor - ACE editor instance - * @param {String} type - Type of completion ('variables' or 'secrets') - * @returns {Promise} - */ - async showCustomCompletions(editor, type = "variables") { - const cursor = editor.getCursorPosition(); - const session = editor.getSession(); - const line = session.getLine(cursor.row); - const textBeforeCursor = line.substring(0, cursor.column); - - let items = []; - let triggerLength = 0; - - if (type === "secrets") { - // Handle secrets - await this.loadSecrets(); - - if (!this.secrets.length) { - return; - } - - items = this.secrets; - } else { - // Handle variables - await this.loadVariables(); - - if (!this.variables.length) { - return; - } - - items = this.variables; - // Check if we're already in a variable context - const isInVariableContext = textBeforeCursor.endsWith("{{"); - - if (isInVariableContext) { - triggerLength = 2; - } - } - - const position = this.calculatePopupPosition(editor, cursor); - - // Set popup type in state - this.state.popupType = type; - - await this.showAutocompletePopup(items, position, editor, triggerLength, type); - } - - /** - * Calculate the optimal position for the autocomplete popup - * @param {Object} editor - ACE editor instance - * @param {Object} cursor - Cursor position object - * @returns {Object} Position object with left and top coordinates - */ - calculatePopupPosition(editor, cursor) { - const renderer = editor.renderer; - - // Calculate cursor position within the editor - const cursorPixelPos = renderer.textToScreenCoordinates( - cursor.row, - cursor.column - ); - - // Get scroll position - const scrollTop = window.pageYOffset || document.documentElement.scrollTop; - const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft; - - // Calculate the cursor position relative to the viewport - const viewportLeft = cursorPixelPos.pageX - scrollLeft; - const viewportTop = cursorPixelPos.pageY - scrollTop; - - // Position popup just below the cursor - const finalLeft = viewportLeft; - const finalTop = viewportTop + renderer.lineHeight; - - // Ensure popup doesn't go outside viewport - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; - const popup = document.querySelector(".ace-autocomplete-popup"); - const popupWidth = popup ? popup.offsetWidth : POPUP_FALLBACK_WIDTH; - const popupHeight = popup ? popup.offsetHeight : POPUP_FALLBACK_HEIGHT; - - let adjustedLeft = finalLeft; - let adjustedTop = finalTop; - - // Adjust if popup would go off-screen horizontally - if (finalLeft + popupWidth > viewportWidth) { - adjustedLeft = finalLeft - popupWidth; - } - - // Adjust if popup would go off-screen vertically - if (finalTop + popupHeight > viewportHeight) { - adjustedTop = finalTop - popupHeight - renderer.lineHeight; - } - - // Make sure popup is not positioned off-screen - adjustedLeft = Math.max(0, adjustedLeft); - adjustedTop = Math.max(0, adjustedTop); - - return { - left: adjustedLeft, - top: adjustedTop, - }; - } - - /** - * Display the autocomplete popup with variables or secrets at the specified position - * @param {Array} items - Array of available variables or secrets - * @param {Object} position - Position object with left and top coordinates - * @param {Object} editor - ACE editor instance - * @param {Number} triggerLength - Length of trigger text that should be replaced - * @param {String} type - Type of completion ('variables' or 'secrets') - * @returns {Promise} - */ - async showAutocompletePopup( - items, - position, - editor, - triggerLength, - type = "variables" - ) { - this.hideAutocompletePopup(); - - this.state.popupItems = items; - this.state.popupPosition = position; - this.state.showPopup = true; - this.state.selectedIndex = 0; - this.state.popupType = type; - this.currentEditor = editor; - this.currentTriggerLength = triggerLength; - this.currentType = type; - - // Add click outside listener - this.clickOutsideListener = (event) => { - // Check if click is outside the popup and ace editor - const popupElement = document.querySelector(".ace-autocomplete-popup"); - const aceElement = this.aceEditor.container; - - if ( - popupElement && - !popupElement.contains(event.target) && - aceElement && - !aceElement.contains(event.target) - ) { - this.hideAutocompletePopup(); - } - }; - - setTimeout(() => { - document.addEventListener("click", this.clickOutsideListener, true); - }, 0); - } - - /** - * Hide the autocomplete popup and clean up event listeners - */ - hideAutocompletePopup() { - // Remove click outside listener - if (this.clickOutsideListener) { - document.removeEventListener("click", this.clickOutsideListener, true); - this.clickOutsideListener = null; - } - - this.state.showPopup = false; - this.state.popupVariables = []; - this.currentEditor = null; - this.state.selectedIndex = 0; - - // Return focus to the ACE editor - if (this.aceEditor) { - this.aceEditor.focus(); - } - } - - /** - * Update the selected index in the autocomplete popup - * @param {Number} index - New selected index - */ - updateSelectedIndex(index) { - if (this.state) { - this.state.selectedIndex = index; - } - } - - /** - * Handle selection of a command from the autocomplete popup - * @param {Object} command - Selected command object - * @param {Object} editor - ACE editor instance - */ - handleCommandSelection(command, editor) { - if (!command || !command.reference) { - this.hideAutocompletePopup(); - return; - } - - const cursor = editor.getCursorPosition(); - const session = editor.getSession(); - const line = session.getLine(cursor.row); - const textBeforeCursor = line.substring(0, cursor.column); - - // Get line length for validation - const lineLength = session.getLine(cursor.row).length; - const currentType = this.currentType || this.state.popupType; - - let range = null; - let insertText = ""; - - if (currentType === "secrets") { - // Handle secrets insertion - // Check if we're inside a secret context (between #!cxtower.secret and !#) - const lastSecretStart = textBeforeCursor.lastIndexOf("#!cxtower.secret"); - const lastSecretEnd = textBeforeCursor.lastIndexOf("!#"); - - // Count occurrences of start and end delimiters for more robust validation - const startCount = (textBeforeCursor.match(/#!cxtower\.secret/g) || []) - .length; - const endCount = (textBeforeCursor.match(/!#/g) || []).length; - const isInsideSecret = - startCount > endCount && - lastSecretStart > lastSecretEnd && - lastSecretStart !== -1; - - if (isInsideSecret) { - // We're inside a secret context, replace from after #!cxtower to cursor - range = { - start: {row: cursor.row, column: lastSecretStart + 16}, - end: {row: cursor.row, column: cursor.column}, - }; - // Clamp range to valid bounds - range.start.column = Math.max( - 0, - Math.min(range.start.column, lineLength) - ); - range.end.column = Math.max( - range.start.column, - Math.min(range.end.column, lineLength) - ); - insertText = `${command.reference}!#`; - } else { - // We're not in a secret context, insert complete secret - const triggerLength = this.currentTriggerLength || 0; - range = { - start: {row: cursor.row, column: cursor.column - triggerLength}, - end: {row: cursor.row, column: cursor.column}, - }; - // Clamp range to valid bounds - range.start.column = Math.max( - 0, - Math.min(range.start.column, lineLength) - ); - range.end.column = Math.max( - range.start.column, - Math.min(range.end.column, lineLength) - ); - insertText = `#!cxtower.secret.${command.reference}!#`; - } - } else { - // Handle variables insertion (existing logic) - const lastOpenBrace = textBeforeCursor.lastIndexOf("{{"); - const lastCloseBrace = textBeforeCursor.lastIndexOf("}}"); - const isInsideVariable = - lastOpenBrace > lastCloseBrace && lastOpenBrace !== -1; - - if (isInsideVariable) { - // We're inside a variable context, replace from after {{ to cursor - range = { - start: {row: cursor.row, column: lastOpenBrace + 2}, - end: {row: cursor.row, column: cursor.column}, - }; - // Clamp range to valid bounds - range.start.column = Math.max( - 0, - Math.min(range.start.column, lineLength) - ); - range.end.column = Math.max( - range.start.column, - Math.min(range.end.column, lineLength) - ); - insertText = ` ${command.reference} `; - } else { - // We're not in a variable context, insert complete variable - const triggerLength = this.currentTriggerLength || 0; - range = { - start: {row: cursor.row, column: cursor.column - triggerLength}, - end: {row: cursor.row, column: cursor.column}, - }; - // Clamp range to valid bounds - range.start.column = Math.max( - 0, - Math.min(range.start.column, lineLength) - ); - range.end.column = Math.max( - range.start.column, - Math.min(range.end.column, lineLength) - ); - insertText = `{{ ${command.reference} }}`; - } - } - - // Replace the text - session.replace(range, insertText); - - // Get the updated line length after replacement - const updatedLineLength = session.getLine(cursor.row).length; - - // Position cursor after the inserted text - const newCursor = { - row: cursor.row, - column: range.start.column + insertText.length, - }; - - newCursor.column = Math.max(0, Math.min(newCursor.column, updatedLineLength)); - - editor.moveCursorToPosition(newCursor); - - this.hideAutocompletePopup(); - editor.focus(); - } - - /** - * Clean up resources when component is destroyed - */ - destroy() { - if (this.inputTimeout) { - clearTimeout(this.inputTimeout); - } - if (this.aceEditor && this.inputListener) { - this.aceEditor.off("input", this.inputListener); - } - this.hideAutocompletePopup(); - super.destroy(); - } -} - -AceCommandField.template = "cetmix_tower_server.AceCommandField"; -AceCommandField.components = { - AutocompletePopup, -}; - -registry.category("fields").add("ace_tower", AceCommandField); - -export {AceCommandField}; diff --git a/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.scss b/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.scss deleted file mode 100644 index 068e05d..0000000 --- a/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.scss +++ /dev/null @@ -1,44 +0,0 @@ -// Custom styles ONLY for AceCommandField - more specific selectors -.o_field_widget.o_field_ace_tower { - display: block !important; -} - -.o_field_widget[data-field-name] .o_field_ace.ace-command-field { - min-height: 200px !important; - height: auto !important; - width: 100% !important; - display: block !important; - - .ace_editor { - min-height: 200px !important; - height: auto !important; - width: 100% !important; - border: 1px solid #ced4da; - border-radius: 4px; - display: block !important; - } - - .ace_content { - min-height: 200px !important; - width: 100% !important; - } - - .ace_scroller { - width: 100% !important; - // Remove any scroll restrictions that might affect standard ACE - overflow: auto !important; - } -} - -// Custom autocomplete popup styles -.ace-autocomplete-popup { - .ace-autocomplete-item { - &:hover { - background-color: #e6f3ff !important; - } - - &:last-child { - border-bottom: none !important; - } - } -} diff --git a/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.xml b/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.xml deleted file mode 100644 index 69503c5..0000000 --- a/addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.esm.js b/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.esm.js deleted file mode 100644 index 136f91f..0000000 --- a/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.esm.js +++ /dev/null @@ -1,317 +0,0 @@ -/** @odoo-module **/ - -import {Component, useEffect, useRef, useState} from "@odoo/owl"; - -class AutocompletePopup extends Component { - /** - * Component setup method that initializes refs, state, and effects - */ - setup() { - this.popupRef = useRef("popupRef"); - this.searchInput = useRef("searchInput"); - this.itemsContainer = useRef("itemsContainer"); - - // State for search functionality - this.state = useState({ - searchTerm: "", - }); - - useEffect( - () => { - this.scrollToSelected(); - }, - () => [this.props.selectedIndex] - ); - - // Auto-focus search input when popup opens - useEffect( - () => { - if (this.searchInput.el) { - // Use setTimeout to ensure DOM is ready - const timeoutId = setTimeout(() => { - this.searchInput.el.focus(); - }, 0); - return () => clearTimeout(timeoutId); - } - }, - () => [] - ); - - useEffect( - () => { - if (this.props.position) { - const timeoutId = setTimeout(() => { - if (this.popupRef.el) { - this.popupRef.el.style.left = `${this.props.position.left}px`; - this.popupRef.el.style.top = `${this.props.position.top}px`; - this.popupRef.el.style.position = "fixed"; - } - }, 0); - return () => clearTimeout(timeoutId); - } - }, - () => [this.props.position] - ); - - // Cleanup effect to clear search timeout - useEffect( - () => { - return () => { - if (this.searchTimeout) { - clearTimeout(this.searchTimeout); - } - }; - }, - () => [] - ); - } - - /** - * Updates search term from external keyboard input (from editor) - * @param {String} char - The character typed or 'Backspace' for deletion - */ - updateSearchFromEditor(char) { - if (char === "Backspace") { - this.state.searchTerm = this.state.searchTerm.slice(0, -1); - } else if (char.length === 1) { - this.state.searchTerm += char; - } - if (this.props.onSelectedIndexChange) { - this.props.onSelectedIndexChange(0); - } - } - - /** - * Filters commands based on search term with enhanced search capabilities - * @returns {Array} Filtered and sorted array of commands matching the search term - */ - get filteredCommands() { - if (!this.state.searchTerm.trim()) { - return this.props.commands; - } - - const searchTerm = this.state.searchTerm.toLowerCase(); - - // Filter and score commands based on search relevance - const scoredCommands = this.props.commands - .map((command) => { - const name = (command.name || "").toLowerCase(); - const reference = (command.reference || "").toLowerCase(); - - let score = 0; - - // Exact matches get highest priority - if (name === searchTerm || reference === searchTerm) { - score = 1000; - } - // Starts with search term gets high priority - else if ( - name.startsWith(searchTerm) || - reference.startsWith(searchTerm) - ) { - score = 100; - } - // Contains search term gets medium priority - else if (name.includes(searchTerm) || reference.includes(searchTerm)) { - score = 10; - } - // No match - else { - return null; - } - - // Boost score for name matches over reference matches - if (name.includes(searchTerm)) { - score += 5; - } - - // Boost score for shorter matches (more relevant) - score += Math.max(0, 50 - Math.min(name.length, reference.length)); - - return {command, score}; - }) - .filter((item) => item !== null) - .sort((a, b) => b.score - a.score) - .map((item) => item.command); - - return scoredCommands; - } - - /** - * Debounces the search filtering - * @param {String} searchTerm - The search term to set - */ - debouncedSearch(searchTerm) { - if (this.searchTimeout) { - clearTimeout(this.searchTimeout); - } - this.searchTimeout = setTimeout(() => { - this.state.searchTerm = searchTerm; - if (this.props.onSelectedIndexChange) { - this.props.onSelectedIndexChange(0); - } - }, 150); - } - - /** - * Handles search input changes - * @param {Event} ev - The input event - */ - onSearchInput(ev) { - ev.stopPropagation(); - this.debouncedSearch(ev.target.value); - } - - /** - * Common keyboard navigation logic - * @param {KeyboardEvent} ev - The keyboard event - */ - handleKeyboardNavigation(ev) { - if (ev.key === "ArrowDown") { - ev.preventDefault(); - const newIndex = Math.min( - (this.props.selectedIndex || 0) + 1, - this.filteredCommands.length - 1 - ); - if (this.props.onSelectedIndexChange) { - this.props.onSelectedIndexChange(newIndex); - } - this.scrollToSelected(); - } else if (ev.key === "ArrowUp") { - ev.preventDefault(); - const newIndex = Math.max((this.props.selectedIndex || 0) - 1, 0); - if (this.props.onSelectedIndexChange) { - this.props.onSelectedIndexChange(newIndex); - } - this.scrollToSelected(); - } else if (ev.key === "Enter") { - ev.preventDefault(); - const selectedCommand = - this.filteredCommands[this.props.selectedIndex || 0]; - if (selectedCommand) { - this.onItemClick(selectedCommand); - } - } else if (ev.key === "Escape") { - ev.preventDefault(); - this.props.onItemClick(null); - } - } - - /** - * Handles keydown events on search input - * @param {KeyboardEvent} ev - The keyboard event - */ - onSearchKeyDown(ev) { - ev.stopPropagation(); - this.handleKeyboardNavigation(ev); - } - - /** - * Handles focus events on search input - * @param {FocusEvent} ev - The focus event - */ - onSearchFocus(ev) { - ev.stopPropagation(); - } - - /** - * Handles blur events on search input - * @param {FocusEvent} ev - The blur event - */ - onSearchBlur(ev) { - ev.stopPropagation(); - } - - /** - * Handles click events on search input - * @param {MouseEvent} ev - The click event - */ - onSearchClick(ev) { - ev.stopPropagation(); - } - - /** - * Handles mousedown events on search input - * @param {MouseEvent} ev - The mousedown event - */ - onSearchMouseDown(ev) { - ev.stopPropagation(); - } - - /** - * Handles item click events - * @param {Object} command - The selected command object - */ - onItemClick(command) { - this.props.onItemClick(command); - } - - /** - * Handles close button click events - */ - onCloseClick() { - this.props.onItemClick(null); - } - - /** - * Handles global keydown events for the popup - * @param {KeyboardEvent} ev - The keyboard event - */ - onKeyDown(ev) { - // Handle search input from editor keyboard events - if (ev.key.length === 1 && ev.key.match(/[a-zA-Z0-9_]/)) { - // Add typed character to search - this.updateSearchFromEditor(ev.key); - } else if (ev.key === "Backspace") { - // Remove last character from search - this.updateSearchFromEditor("Backspace"); - } else { - // Use common keyboard navigation logic - this.handleKeyboardNavigation(ev); - } - } - - /** - * Scrolls the selected item into view - */ - scrollToSelected() { - const itemsContainer = this.itemsContainer.el; - if ( - itemsContainer && - this.props.selectedIndex !== undefined && - this.props.selectedIndex >= 0 && - this.props.selectedIndex < itemsContainer.children.length - ) { - const selectedItem = itemsContainer.children[this.props.selectedIndex]; - if (selectedItem) { - selectedItem.scrollIntoView({ - block: "nearest", - behavior: "smooth", - }); - } - } - } - - /** - * Returns CSS class for autocomplete item based on selection state - * @param {Number} index - The item index - * @returns {String} CSS class string - */ - getItemClass(index) { - return index === (this.props.selectedIndex || 0) - ? "ace-autocomplete-item ace-autocomplete-item-selected" - : "ace-autocomplete-item"; - } -} - -AutocompletePopup.template = "cetmix_tower_server.AutocompletePopup"; -AutocompletePopup.props = { - commands: {type: Array}, - onItemClick: {type: Function}, - position: {type: Object}, - selectedIndex: {type: Number, optional: true}, - onSelectedIndexChange: {type: Function, optional: true}, - type: {type: String, optional: true}, -}; - -export {AutocompletePopup}; diff --git a/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.scss b/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.scss deleted file mode 100644 index e2c4fd6..0000000 --- a/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.scss +++ /dev/null @@ -1,190 +0,0 @@ -// Define z-index variable for better management -$z-index-autocomplete: 1050; // Above dropdowns but below modals - -.ace-autocomplete-popup { - position: absolute; // Keep original positioning for cursor placement - background: white; - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); - z-index: $z-index-autocomplete; - min-width: 300px; - max-width: 500px; - max-height: 300px; - overflow: hidden; - font-family: monospace; - font-size: 14px; - - // Mobile adaptations - @media (max-width: 768px) { - width: 90%; - min-width: unset; - max-width: unset; - left: 50% !important; - transform: translateX(-50%); - font-size: 13px; - } -} - -.ace-autocomplete-search { - padding: 8px; - padding-right: 48px; // Add right padding to avoid overlap with close button - border-bottom: 1px solid #eee; - background: #f8f9fa; - - // Mobile: reduce padding - @media (max-width: 768px) { - padding: 6px; - padding-right: 56px; - } -} - -.ace-autocomplete-search-input { - width: 100%; - padding: 6px 10px; - border: 1px solid #ddd; - border-radius: 3px; - font-size: 13px; - outline: none; - box-sizing: border-box; -} - -.ace-autocomplete-search-input:focus { - border-color: #007cba; - box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.1); -} - -.ace-autocomplete-items { - max-height: 240px; - overflow-y: auto; - - /* Standard scrollbar styling (Firefox 64+) */ - scrollbar-width: thin; - scrollbar-color: #c1c1c1 #f1f1f1; -} - -/* Scrollbar styling for webkit browsers */ -.ace-autocomplete-items::-webkit-scrollbar { - width: 6px; -} - -.ace-autocomplete-items::-webkit-scrollbar-track { - background: #f1f1f1; -} - -.ace-autocomplete-items::-webkit-scrollbar-thumb { - background: #c1c1c1; - border-radius: 3px; -} - -.ace-autocomplete-items::-webkit-scrollbar-thumb:hover { - background: #a8a8a8; -} - -.ace-autocomplete-item { - padding: 8px 12px; - cursor: pointer; - border-bottom: 1px solid #f0f0f0; - display: flex; - justify-content: space-between; - align-items: center; - - // Mobile: stack items vertically with reduced padding - @media (max-width: 768px) { - flex-direction: column; - align-items: flex-start; - padding: 6px 12px; - } -} - -.ace-autocomplete-item:hover { - background-color: #f5f5f5; -} - -.ace-autocomplete-item-selected { - background-color: #e6f3ff; - color: #0066cc; -} - -.ace-autocomplete-item-selected:hover { - background-color: #cce7ff; -} - -.command-name { - font-weight: 600; - color: #333; - flex: 1; - margin-right: 10px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - // Mobile adaptations - @media (max-width: 768px) { - margin-right: 0; - margin-bottom: 2px; // Reduced from 4px - width: 100%; - font-size: 14px; - } -} - -.command-description { - color: #666; - font-size: 12px; - font-style: italic; - flex-shrink: 0; - - // Mobile adaptations - @media (max-width: 768px) { - font-size: 11px; - width: 100%; - word-break: break-word; - } -} - -.ace-autocomplete-no-results { - padding: 16px 12px; - text-align: center; - color: #999; - font-style: italic; -} - -// Close button styles -.ace-autocomplete-close-btn { - position: absolute; - top: 8px; - right: 8px; - background: none; - border: none; - font-size: 20px; - font-weight: bold; - color: #666; - cursor: pointer; - padding: 4px 8px; - line-height: 1; - border-radius: 3px; - z-index: 1; - min-width: 30px; - min-height: 30px; - display: flex; - align-items: center; - justify-content: center; - - &:hover { - background-color: #f0f0f0; - color: #333; - } - - &:active { - background-color: #e0e0e0; - } - - // Mobile-friendly touch target - @media (max-width: 768px) { - min-width: 40px; - min-height: 40px; - font-size: 24px; - top: 4px; - right: 4px; - } -} diff --git a/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml b/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml deleted file mode 100644 index ecff260..0000000 --- a/addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - -
    - - - - - -
    -
    - - -
    -
    - No secrets found - No variables found -
    -
    -
    -
    -
    diff --git a/addons/cetmix_tower_server/static/src/components/server_status/server_status_field.esm.js b/addons/cetmix_tower_server/static/src/components/server_status/server_status_field.esm.js deleted file mode 100644 index 42d274e..0000000 --- a/addons/cetmix_tower_server/static/src/components/server_status/server_status_field.esm.js +++ /dev/null @@ -1,33 +0,0 @@ -/** @odoo-module */ - -import {registry} from "@web/core/registry"; -import {StateSelectionField} from "@web/views/fields/state_selection/state_selection_field"; - -import {STATUS_COLORS, STATUS_COLOR_PREFIX} from "../../utils/server_utils.esm"; - -export class ServerStatusField extends StateSelectionField { - /** - * @override - */ - setup() { - super.setup(); - this.colorPrefix = STATUS_COLOR_PREFIX; - this.colors = STATUS_COLORS; - } - - /** - * @override - */ - get options() { - return [[false, "Undefined"], ...super.options]; - } - - /** - * @override - */ - get showLabel() { - return !this.props.hideLabel; - } -} - -registry.category("fields").add("server_status", ServerStatusField); diff --git a/addons/cetmix_tower_server/static/src/components/server_status/server_status_field.scss b/addons/cetmix_tower_server/static/src/components/server_status/server_status_field.scss deleted file mode 100644 index 2562835..0000000 --- a/addons/cetmix_tower_server/static/src/components/server_status/server_status_field.scss +++ /dev/null @@ -1,33 +0,0 @@ -.o_server_status_bubble { - @extend .o_status; - - &.o_color_server_status_bubble_info { - background-color: $o-info; - } - &.o_color_server_status_bubble_success { - background-color: $o-success; - } - &.o_color_server_status_bubble_danger { - background-color: $o-danger; - } - &.o_color_server_status_bubble_warning { - background-color: $o-warning; - } -} -.o_field_server_status { - display: flex; - justify-content: space-between; - align-items: center; - padding: 4px 8px; - margin: 0px 16px; - border-radius: 5px; - border: 1px solid #e5e5e5; - width: fit-content !important; - - .o_status_label { - color: #4c4c4c; - font-size: 14px; - margin-left: 0.5rem !important; - display: block; - } -} diff --git a/addons/cetmix_tower_server/static/src/utils/server_utils.esm.js b/addons/cetmix_tower_server/static/src/utils/server_utils.esm.js deleted file mode 100644 index 158d82a..0000000 --- a/addons/cetmix_tower_server/static/src/utils/server_utils.esm.js +++ /dev/null @@ -1,17 +0,0 @@ -/** @odoo-module */ - -/** - * List of colors according to the selection value - */ -export const STATUS_COLORS = { - false: "info", - stopped: "danger", - starting: "warning", - running: "success", - stopping: "warning", - restarting: "warning", - delete_error: "danger", -}; - -export const STATUS_COLOR_PREFIX = - "o_server_status_bubble mx-0 o_color_server_status_bubble_"; diff --git a/addons/cetmix_tower_server/tests/__init__.py b/addons/cetmix_tower_server/tests/__init__.py deleted file mode 100644 index 72d0064..0000000 --- a/addons/cetmix_tower_server/tests/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -from . import test_server -from . import test_command -from . import test_file -from . import test_file_template -from . import test_plan -from . import test_plan_line -from . import test_plan_line_action -from . import test_command_log -from . import test_plan_log -from . import test_server_log -from . import test_server_template -from . import test_variable -from . import test_variable_value -from . import test_variable_option -from . import test_command_wizard -from . import test_reference_mixin -from . import test_scheduled_task -from . import test_update_related_variable_names -from . import test_key -from . import test_cetmix_tower -from . import test_tag -from . import test_shortcut -from . import test_tools -from . import test_partner_server_btn -from . import test_vault_mixin -from . import test_tag_mixin diff --git a/addons/cetmix_tower_server/tests/common.py b/addons/cetmix_tower_server/tests/common.py deleted file mode 100644 index 2f93a5f..0000000 --- a/addons/cetmix_tower_server/tests/common.py +++ /dev/null @@ -1,433 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import os -from unittest.mock import MagicMock, patch - -from odoo import _ -from odoo.exceptions import ValidationError - -from odoo.addons.base.tests.common import BaseCommon - -from ..models.constants import GENERAL_ERROR -from ..ssh.ssh import SftpService, SSHConnection - - -class TestTowerCommon(BaseCommon): - """ - Common test class for Cetmix Tower. - """ - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # ---------------------------------------------- - # -- Create core elements invoked in the tests - # ---------------------------------------------- - # Group XML records - cls.group_user = cls.env.ref("cetmix_tower_server.group_user") - cls.group_manager = cls.env.ref("cetmix_tower_server.group_manager") - cls.group_root = cls.env.ref("cetmix_tower_server.group_root") - - # Cetmix Tower helper model - cls.CetmixTower = cls.env["cetmix.tower"] - - # Tags - cls.Tag = cls.env["cx.tower.tag"] - cls.tag_test_staging = cls.Tag.create({"name": "Test Staging"}) - cls.tag_test_production = cls.Tag.create({"name": "Test Production"}) - - # Users - cls.Users = cls.env["res.users"] - cls.user_bob = cls.Users.create( - { - "name": "Bob", - "login": "bob", - "groups_id": [(4, cls.env.ref("base.group_user").id)], - } - ) - cls.user = cls.Users.create( - { - "name": "Test User", - "login": "test_user", - "email": "test_user@example.com", - "groups_id": [(6, 0, [cls.group_user.id])], - } - ) - cls.manager = cls.Users.create( - { - "name": "Test Manager", - "login": "test_manager", - "email": "test_manager@example.com", - "groups_id": [(6, 0, [cls.group_manager.id])], - } - ) - cls.root = cls.Users.create( - { - "name": "Test Root", - "login": "test_root", - "email": "test_root@example.com", - "groups_id": [(6, 0, [cls.group_root.id])], - } - ) - - # OS - cls.os_debian_10 = cls.env["cx.tower.os"].create({"name": "Test Debian 10"}) - - # Server - cls.Server = cls.env["cx.tower.server"] - cls.server_test_1 = cls.Server.create( - { - "name": "Test 1", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "host_key": "test_key", - "os_id": cls.os_debian_10.id, - } - ) - - # Server Template - cls.ServerTemplate = cls.env["cx.tower.server.template"] - cls.server_template_sample = cls.ServerTemplate.create( - { - "name": "Sample Template", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": cls.os_debian_10.id, - } - ) - - # Server log - cls.ServerLog = cls.env["cx.tower.server.log"] - - # Variable - cls.Variable = cls.env["cx.tower.variable"] - cls.VariableValue = cls.env["cx.tower.variable.value"] - cls.VariableOption = cls.env["cx.tower.variable.option"] - - cls.variable_path = cls.Variable.create({"name": "test_path_"}) - cls.variable_dir = cls.Variable.create({"name": "test_dir"}) - cls.variable_os = cls.Variable.create({"name": "test_os"}) - cls.variable_url = cls.Variable.create({"name": "test_url"}) - cls.variable_version = cls.Variable.create({"name": "test_version"}) - - # Key - cls.Key = cls.env["cx.tower.key"] - cls.KeyValue = cls.env["cx.tower.key.value"] - - cls.key_1 = cls.Key.create( - {"name": "Test Key 1", "key_type": "k", "secret_value": "much key"} - ) - cls.secret_2 = cls.Key.create( - {"name": "Test Key 2", "key_type": "s", "secret_value": "secret top"} - ) - - # Command - cls.sudo_prefix = "sudo -S -p ''" - cls.Command = cls.env["cx.tower.command"] - cls.command_create_dir = cls.Command.create( - { - "name": "Test create directory", - "path": "/home/{{ tower.server.username }}", - "code": "cd {{ test_path_ }} && mkdir {{ test_dir }}", - } - ) - cls.command_list_dir = cls.Command.create( - { - "name": "Test create directory", - "path": "/home/{{ tower.server.username }}", - "code": "cd {{ test_path_ }} && ls -l", - } - ) - - cls.template_file_tower = cls.env["cx.tower.file.template"].create( - { - "name": "Test file template", - "file_name": "test_os.txt", - "source": "tower", - "server_dir": "/home/{{ tower.server.username }}", - "code": "Hello, world!", - } - ) - - cls.template_file_server = cls.env["cx.tower.file.template"].create( - { - "name": "Test file template", - "file_name": "test_os.txt", - "source": "server", - "server_dir": "/home/{{ tower.server.username }}", - } - ) - - cls.command_create_file_with_template_tower_source = cls.Command.create( - { - "name": "Test create file with template with tower source", - "path": "/home/{{ tower.server.username }}", - "action": "file_using_template", - "file_template_id": cls.template_file_tower.id, - "if_file_exists": "raise", - } - ) - - cls.command_create_file_with_template_server_source = cls.Command.create( - { - "name": "Test create file with template with server source", - "path": "/home/{{ tower.server.username }}", - "action": "file_using_template", - "file_template_id": cls.template_file_server.id, - "if_file_exists": "raise", - } - ) - - # Command log - cls.CommandLog = cls.env["cx.tower.command.log"] - - # File template - cls.FileTemplate = cls.env["cx.tower.file.template"] - - # File - cls.File = cls.env["cx.tower.file"] - - # Flight Plans - cls.Plan = cls.env["cx.tower.plan"] - cls.plan_line = cls.env["cx.tower.plan.line"] - cls.plan_line_action = cls.env["cx.tower.plan.line.action"] - - cls.plan_1 = cls.Plan.create( - { - "name": "Test plan 1", - "note": "Create directory and list its content", - "tag_ids": [(6, 0, [cls.tag_test_staging.id])], - } - ) - cls.plan_line_1 = cls.plan_line.create( - { - "sequence": 5, - "plan_id": cls.plan_1.id, - "command_id": cls.command_create_dir.id, - "path": "/such/much/path", - } - ) - cls.plan_line_2 = cls.plan_line.create( - { - "sequence": 20, - "plan_id": cls.plan_1.id, - "command_id": cls.command_list_dir.id, - } - ) - cls.plan_line_1_action_1 = cls.plan_line_action.create( - { - "line_id": cls.plan_line_1.id, - "sequence": 1, - "condition": "==", - "value_char": "0", - } - ) - cls.plan_line_1_action_2 = cls.plan_line_action.create( - { - "line_id": cls.plan_line_1.id, - "sequence": 2, - "condition": ">", - "value_char": "0", - "action": "ec", - "custom_exit_code": 255, - } - ) - cls.plan_line_2_action_1 = cls.plan_line_action.create( - { - "line_id": cls.plan_line_2.id, - "sequence": 1, - "condition": "==", - "value_char": "-1", - "action": "ec", - "custom_exit_code": 100, - } - ) - cls.plan_line_2_action_2 = cls.plan_line_action.create( - { - "line_id": cls.plan_line_2.id, - "sequence": 2, - "condition": ">=", - "value_char": "3", - "action": "n", - } - ) - - # Flight plan log - cls.PlanLog = cls.env["cx.tower.plan.log"] - - # Shortcut - cls.Shortcut = cls.env["cx.tower.shortcut"] - - # Model references - cls.OS = cls.env["cx.tower.os"] - cls.PlanLineAction = cls.env["cx.tower.plan.line.action"] - - # Scheduled task - cls.ScheduledTask = cls.env["cx.tower.scheduled.task"] - cls.ScheduledTaskCv = cls.env["cx.tower.scheduled.task.cv"] - - # apply ssh connection patches - cls.apply_patches() - - @classmethod - def apply_patches(cls): - """ - Apply mock patches for SSH-related methods to simulate various - scenarios during testing. - - Patches: - 1. SSHConnection.connect: - - Returns a mock connection with a fake exec_command method, - which returns a successful or unsuccessful result depending on the - command content. - 2. SftpService.download_file: - - Returns b"ok\x00" for files with the .zip extension and - b"ok" for the rest. - 3. SftpService.upload_file: - - Returns MagicMock, simulating file upload. - 4. SftpService.delete_file: - - Returns MagicMock, simulating file deletion. - """ - - # Patch connection SSH method - def ssh_connect(self): - connection_mock = MagicMock() - - # set up stdin with a condition for error simulation - def exec_command_side_effect(command, *args, **kwargs): - # Create mocks for stdin, stdout, and stderr - stdin_mock = MagicMock() - stdout_mock = MagicMock() - stderr_mock = MagicMock() - - if "fail" in command: - # Simulate failure - stdout_mock.channel.recv_exit_status.return_value = GENERAL_ERROR - stdout_mock.readlines.return_value = [] - stderr_mock.readlines.return_value = ["error"] - return stdin_mock, stdout_mock, stderr_mock - elif "raise" in command: - # Simulate an exception - raise Exception("error") # pylint: disable=broad-exception-raised - else: - # Simulate success - stdout_mock.channel.recv_exit_status.return_value = 0 - stdout_mock.readlines.return_value = ["ok"] - stderr_mock.readlines.return_value = [] - return stdin_mock, stdout_mock, stderr_mock - - # Apply side effect to exec_command - connection_mock.exec_command.side_effect = exec_command_side_effect - - return connection_mock - - connect_patch = patch.object(SSHConnection, "connect", new=ssh_connect) - connect_patch.start() - cls.addClassCleanup(connect_patch.stop) - - # Patch file manipulation methods for testing - def ssh_download_file(self, remote_path): - if hasattr(self, "env"): - error = self.env.context.get("raise_download_error") - if error: - raise ValidationError(error) - - _, extension = os.path.splitext(remote_path) - if extension == ".zip": - return b"ok\x00" - return b"ok" - - download_patch = patch.object( - SftpService, "download_file", new=ssh_download_file - ) - download_patch.start() - cls.addClassCleanup(download_patch.stop) - - def ssh_upload_file(self, file, remote_path): - if hasattr(self, "env"): - error = self.env.context.get("raise_upload_error") - if error: - raise ValidationError(error) - return MagicMock() - - upload_patch = patch.object(SftpService, "upload_file", new=ssh_upload_file) - upload_patch.start() - cls.addClassCleanup(upload_patch.stop) - - def ssh_delete_file(self, remote_path): - return MagicMock() - - delete_patch = patch.object(SftpService, "delete_file", new=ssh_delete_file) - delete_patch.start() - cls.addClassCleanup(delete_patch.stop) - - @classmethod - def add_to_group(cls, user, group_refs): - """Add user to groups - - Args: - user (res.users): User record - group_refs (list): Group ref OR List of group references - eg ['base.group_user', 'some_module.some_group'...] - """ - if isinstance(group_refs, str): - group = cls.env.ref(group_refs, raise_if_not_found=False) - if not group: - raise ValidationError(_("Group reference %s not found!") % group_refs) - action = [(4, group.id)] - elif isinstance(group_refs, list): - action = [] - for group_ref in group_refs: - group = cls.env.ref(group_ref, raise_if_not_found=False) - if not group: - raise ValidationError( - _("Group reference %s not found!") % group_ref - ) - action.append((4, group.id)) - else: - raise ValidationError(_("groups_ref must be string or list of strings!")) - user.write({"groups_id": action}) - - @classmethod - def remove_from_group(cls, user, group_refs): - """Remove user from groups - - Args: - user (res.users): User record - group_refs (list): List of group references - eg ['base.group_user', 'some_module.some_group'...] - """ - if isinstance(group_refs, str): - group = cls.env.ref(group_refs, raise_if_not_found=False) - if not group: - raise ValidationError(_("Group reference %s not found!") % group_refs) - action = [(3, group.id)] - elif isinstance(group_refs, list): - action = [] - for group_ref in group_refs: - group = cls.env.ref(group_ref, raise_if_not_found=False) - if not group: - raise ValidationError( - _("Group reference %s not found!") % group_ref - ) - action.append((3, group.id)) - else: - raise ValidationError(_("groups_ref must be string or list of strings!")) - user.write({"groups_id": action}) - - @classmethod - def write_and_invalidate(cls, records, **values): - """Write values and invalidate cache - - Args: - records (recordset): recordset to save values - **values (dict): values to set - """ - if values: - records.write(values) - records.invalidate_recordset(values.keys()) diff --git a/addons/cetmix_tower_server/tests/test_cetmix_tower.py b/addons/cetmix_tower_server/tests/test_cetmix_tower.py deleted file mode 100644 index ffbb2a7..0000000 --- a/addons/cetmix_tower_server/tests/test_cetmix_tower.py +++ /dev/null @@ -1,242 +0,0 @@ -# Copyright (C) 2024 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from unittest.mock import patch - -from ..models.constants import GENERAL_ERROR, NOT_FOUND, SSH_CONNECTION_ERROR -from .common import TestTowerCommon - - -class TestCetmixTower(TestTowerCommon): - """ - Tests for the 'cetmix.tower' helper model - """ - - def test_server_set_variable_value(self): - """Test plan line action naming""" - - # -- 1-- - # Create new variable - variable_meme = self.Variable.create( - {"name": "Meme Variable", "reference": "meme_variable"} - ) - - # Set variable for Server 1 - result = self.CetmixTower.server_set_variable_value( - server_reference=self.server_test_1.reference, - variable_reference=variable_meme.reference, - value="Doge", - ) - - # Check exit code - self.assertEqual(result["exit_code"], 0, "Exit code must be equal to 0") - - # Check variable value - variable_value = self.VariableValue.search( - [("variable_id", "=", variable_meme.id)] - ) - - self.assertEqual(len(variable_value), 1, "Must be 1 result") - self.assertEqual(variable_value.value_char, "Doge", "Must be Doge!") - - # -- 2 -- - # Update existing variable value - - # Set variable for Server 1 - result = self.CetmixTower.server_set_variable_value( - server_reference=self.server_test_1.reference, - variable_reference=variable_meme.reference, - value="Pepe", - ) - - # Check exit code - self.assertEqual(result["exit_code"], 0, "Exit code must be equal to 0") - - # Check variable value - variable_value = self.VariableValue.search( - [("variable_id", "=", variable_meme.id)] - ) - - self.assertEqual(len(variable_value), 1, "Must be 1 result") - self.assertEqual(variable_value.value_char, "Pepe", "Must be Pepe!") - - def test_server_get_variable_value(self): - """Test getting value for server""" - variable_meme = self.Variable.create( - {"name": "Meme Variable", "reference": "meme_variable"} - ) - global_value = self.VariableValue.create( - {"variable_id": variable_meme.id, "value_char": "Memes Globalvs"} - ) - - # -- 1 -- Get value for Server with no server value defined - value = self.CetmixTower.server_get_variable_value( - self.server_test_1.reference, variable_meme.reference - ) - self.assertEqual(value, global_value.value_char) - - # -- 2 -- Do not fetch global value now - value = self.CetmixTower.server_get_variable_value( - self.server_test_1.reference, variable_meme.reference, check_global=False - ) - self.assertIsNone(value) - - # -- 3 -- Add server value and try again - server_value = self.VariableValue.create( - { - "variable_id": variable_meme.id, - "value_char": "Memes Servervs", - "server_id": self.server_test_1.id, - } - ) - value = self.CetmixTower.server_get_variable_value( - self.server_test_1.reference, variable_meme.reference - ) - self.assertEqual(value, server_value.value_char) - - def test_server_check_ssh_connection(self): - """ - Test SSH connection check with a mocked function that - either returns a dictionary or raises an exception. - """ - - # Test successful connection - result = self.env["cetmix.tower"].server_check_ssh_connection( - self.server_test_1.reference, - ) - self.assertEqual(result["exit_code"], 0, "SSH connection should be successful.") - - def test_ssh_connection(this, *args, **kwargs): - return {"status": GENERAL_ERROR} - - with patch.object( - self.registry["cx.tower.server"], "test_ssh_connection", test_ssh_connection - ): - # Test connection timeout after max attempts - result = self.env["cetmix.tower"].server_check_ssh_connection( - self.server_test_1.reference, - attempts=2, - wait_time=1, - ) - self.assertEqual( - result["exit_code"], - SSH_CONNECTION_ERROR, - "SSH connection should timeout after maximum attempts.", - ) - - def test_server_run_command(self): - """Test running command on server""" - # Create test command - command = self.Command.create( - { - "name": "Test Command", - "reference": "test_command", - "code": "echo 'Hello World'", - "action": "ssh_command", - } - ) - - # -- 1 -- Test with non-existent server - result = self.CetmixTower.server_run_command( - server_reference="non_existent", - command_reference=command.reference, - ) - self.assertEqual(result["exit_code"], NOT_FOUND) - self.assertEqual(result["message"], "Server not found") - - # -- 2 -- Test with non-existent command - result = self.CetmixTower.server_run_command( - server_reference=self.server_test_1.reference, - command_reference="non_existent", - ) - self.assertEqual(result["exit_code"], NOT_FOUND) - self.assertEqual(result["message"], "Command not found") - - # -- 3 -- Test successful command execution - result = self.CetmixTower.server_run_command( - server_reference=self.server_test_1.reference, - command_reference=command.reference, - ) - self.assertEqual(result["exit_code"], 0) - - def test_server_run_flight_plan(self): - """Test running flight plan on server""" - # Create test flight plan - flight_plan = self.Plan.create( - { - "name": "Test Flight Plan", - "reference": "test_flight_plan", - } - ) - - # -- 1 -- Test with non-existent server - result = self.CetmixTower.server_run_flight_plan( - server_reference="non_existent", - flight_plan_reference=flight_plan.reference, - ) - self.assertFalse(result, "Should return False for non-existent server") - - # -- 2 -- Test with non-existent flight plan - result = self.CetmixTower.server_run_flight_plan( - server_reference=self.server_test_1.reference, - flight_plan_reference="non_existent", - ) - self.assertFalse(result, "Should return False for non-existent flight plan") - - # -- 3 -- Test successful flight plan execution - with patch.object(self.server_test_1.__class__, "run_flight_plan") as mock_run: - # Setup mock to return a plan log record - plan_log = self.PlanLog.create( - { - "name": "Test Log", - "server_id": self.server_test_1.id, - "plan_id": flight_plan.id, - } - ) - mock_run.return_value = plan_log - - # Run flight plan - result = self.CetmixTower.server_run_flight_plan( - server_reference=self.server_test_1.reference, - flight_plan_reference=flight_plan.reference, - ) - - # Verify result - self.assertEqual(result, plan_log, "Should return plan log record") - mock_run.assert_called_once_with(flight_plan) - - def test_server_run_command_with_variable_values(self): - """Test running command with variable values""" - # Create test command - command = self.Command.create( - { - "name": "Test Command", - "reference": "test_command", - "code": "result = {'exit_code': 0, 'message': {{ test_version }}}", - "action": "python_code", - } - ) - # Set variable value for the server - self.CetmixTower.server_set_variable_value( - server_reference=self.server_test_1.reference, - variable_reference=self.variable_version.reference, - value="prod", - ) - - # -- 1 -- - # Run command without modifying variable values - result = self.CetmixTower.server_run_command( - server_reference=self.server_test_1.reference, - command_reference=command.reference, - ) - self.assertEqual(result["exit_code"], 0) - self.assertEqual(result["message"], "prod") - - # -- 2 -- - # Run command with modified variable values - result = self.CetmixTower.server_run_command( - server_reference=self.server_test_1.reference, - command_reference=command.reference, - **{"test_version": "dev"}, - ) - self.assertEqual(result["exit_code"], 0) - self.assertEqual(result["message"], "dev") diff --git a/addons/cetmix_tower_server/tests/test_command.py b/addons/cetmix_tower_server/tests/test_command.py deleted file mode 100644 index b2042ee..0000000 --- a/addons/cetmix_tower_server/tests/test_command.py +++ /dev/null @@ -1,1911 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from datetime import timedelta -from unittest.mock import patch - -from odoo.exceptions import AccessError, ValidationError -from odoo.fields import Datetime -from odoo.tests.common import Form -from odoo.tools import mute_logger - -from ..models.constants import ( - ANOTHER_COMMAND_RUNNING, - COMMAND_TIMED_OUT, - COMMAND_TIMED_OUT_MESSAGE, - GENERAL_ERROR, -) -from .common import TestTowerCommon - - -class TestTowerCommand(TestTowerCommon): - """ - Test the command model. - - Important! - As this model inherits from the `cx.tower.template.mixin` - we will tests template rendering methods in this class too. - - """ - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Save variable values for Server 1 - with Form(cls.server_test_1) as f: - with f.variable_value_ids.new() as line: - line.variable_id = cls.variable_dir - line.value_char = "test-odoo-1" - with f.variable_value_ids.new() as line: - line.variable_id = cls.variable_path - line.value_char = "/opt/tower" - f.save() - - # Secret key - cls.secret_folder_key = cls.Key.create( - { - "name": "Folder", - "reference": "FOLDER", - "secret_value": "secretFolder", - "key_type": "s", - } - ) - cls.secret_python_key = cls.Key.create( - { - "name": "python", - "reference": "PYTHON", - "secret_value": "secretPythonCode", - "key_type": "s", - } - ) - - # secret value as multi line string - cls.python_ssh_key = cls.Key.create( - { - "name": "Test Python SSH Key", - "reference": "test_python_ssh_key", - "key_type": "s", - "secret_value": """ - Python - much - key - """, - } - ) - - cls.secret_test_rsa_key = cls.Key.create( - { - "name": "test rsa", - "reference": "test_rsa", - "secret_value": """-----BEGIN RSA PRIVATE KEY----- -MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu -KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm -o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k -TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7 -9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy -v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs -/5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00 ------END RSA PRIVATE KEY----- """, - "key_type": "s", - } - ) - # Command - cls.command_create_new_command = cls.Command.create( - { - "name": "Create new command", - "action": "python_code", - "code": """ -server_name = {{ tower.server.name }} -if server_name and #!cxtower.secret.FOLDER!# == "secretFolder": - # We don't actually create a new command because it will raise - # access error if user doesn't have access to 'create' operation. - # Instead we just return a dummy command result. - command = "new command" - result = {"exit_code": 0, "message": "New command was created"} -else: - result = {"exit_code": %s, "message": "error"} - """ - % GENERAL_ERROR, - } - ) - - cls.command_python_command_1 = cls.Command.create( - { - "name": "Python command with secret #1", - "action": "python_code", - "code": """ -result = { - "exit_code": 0, - "message": #!cxtower.secret.PYTHON!#, -} - """, - } - ) - - cls.command_python_command_2 = cls.Command.create( - { - "name": "Python command with secret #2", - "action": "python_code", - "code": """ -result = { - "exit_code": 0, - "message": 'We use #!cxtower.secret.PYTHON!#' , -} - """, - } - ) - - cls.command_python_command_3 = cls.Command.create( - { - "name": "Python command with secret #3", - "action": "python_code", - "code": """ -result = { - "exit_code": 0, - "message": ""#!cxtower.secret.test_rsa!#"" , -} - """, - } - ) - - cls.command_python_command_4 = cls.Command.create( - { - "name": "Python command with secret #4", - "action": "python_code", - "code": """ -top_secret = #!cxtower.secret.test_python_ssh_key!# -result = { - "exit_code": 0, - "message": top_secret , -} - """, - } - ) - cls.server = cls.Server.create( - { - "name": "Test Server", - "user_ids": [(6, 0, [cls.user.id])], - "manager_ids": [(6, 0, [cls.manager.id])], - "ssh_username": "test", - "ssh_password": "test", - "ip_v4_address": "127.0.0.1", - } - ) - - def _create_command(self, **kwargs): - """Helper to create a command record with default values.""" - vals = { - "name": "Test Command", - "access_level": "1", # override default - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - "server_ids": [(6, 0, [])], - } - if kwargs: - vals.update(kwargs) - return self.Command.create(vals) - - def test_user_read_access(self): - """ - For a user: - Read access is allowed if access_level == "1" and either the command's - own user_ids includes the user OR a related server (via server_ids) - includes the user in its user_ids. - """ - # Case 1: Command with access_level "1" and user in command.user_ids. - cmd1 = self._create_command( - **{ - "access_level": "1", - "user_ids": [(6, 0, [self.user.id])], - } - ) - recs1 = self.Command.with_user(self.user).search([("id", "=", cmd1.id)]) - self.assertIn( - cmd1, - recs1, - "User should see the command if in command.user_ids" - " and access_level == '1'.", - ) - - # Case 2: Command with access_level "1" and user not in command.user_ids - # but in a related server. - cmd2 = self._create_command( - **{ - "access_level": "1", - "user_ids": [(6, 0, [])], - "server_ids": [(6, 0, [self.server.id])], - } - ) - recs2 = self.Command.with_user(self.user).search([("id", "=", cmd2.id)]) - self.assertIn( - cmd2, - recs2, - "User should see the command if related server.user_ids includes the user.", - ) - - # Negative: If access_level is "1" but neither command.user_ids - # nor server_ids.user_ids includes the user. - cmd3 = self._create_command( - **{ - "access_level": "1", - "user_ids": [(6, 0, [])], - "server_ids": [(6, 0, [])], - } - ) - recs3 = self.Command.with_user(self.user).search([("id", "=", cmd3.id)]) - self.assertNotIn( - cmd3, - recs3, - "User should not see the command if not granted access.", - ) - - def test_manager_read_access(self): - """ - For a manager: - Allowed to read a command if access_level <= "2" AND - (either the command itself grants access via user_ids or manager_ids - OR there are no related servers OR a related server grants access via - its user_ids or manager_ids). - """ - # Case 1: Command with access_level "2" and command.manager_ids - # includes the manager but the server is not related to the command. - another_server = self.Server.create( - { - "name": "Another Server", - "ip_v4_address": "127.0.0.2", - "ssh_username": "test", - "ssh_password": "test", - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - } - ) - cmd1 = self._create_command( - **{ - "access_level": "2", - "manager_ids": [(6, 0, [self.manager.id])], - "server_ids": [(6, 0, [another_server.id])], - } - ) - recs1 = self.Command.with_user(self.manager).search([("id", "=", cmd1.id)]) - self.assertIn( - cmd1, - recs1, - "Manager should see the command if in command.manager_ids" - " and access_level <= '2'.", - ) - - # Case 2: Command with access_level "2" that does not grant access - # on the command itself, but a related server grants access via - # but a related server grants access via its manager_ids. - cmd2 = self._create_command( - **{ - "access_level": "2", - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - "server_ids": [(6, 0, [self.server.id])], - } - ) - recs2 = self.Command.with_user(self.manager).search([("id", "=", cmd2.id)]) - self.assertIn( - cmd2, - recs2, - "Manager should see the command if related server.manager_ids" - " includes the manager.", - ) - - # Positive: Command with access_level "2" without any granted access. - cmd3 = self._create_command( - **{ - "access_level": "2", - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - "server_ids": [(6, 0, [])], - } - ) - recs3 = self.Command.with_user(self.manager).search([("id", "=", cmd3.id)]) - self.assertIn( - cmd3, - recs3, - "Manager should see the command if not granted access " - "but not related to any server.", - ) - - # Case 3: Remove from manager in the cmd1. - # Should not see the command because it belongs to another server. - cmd1.manager_ids = [(3, self.manager.id)] - recs4 = self.Command.with_user(self.manager).search([("id", "=", cmd1.id)]) - self.assertNotIn( - cmd1, - recs4, - "Manager should not see the command if " - "removed from command.manager_ids." - " and command belongs to another server.", - ) - - def test_manager_write_create_access(self): - """ - For a manager: - Allowed to write and create a command if access_level <= "2" AND - the command's own manager_ids includes the manager. - """ - # Case: Command with access_level "2" and manager_ids includes the manager. - cmd1 = self._create_command( - **{ - "access_level": "2", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - try: - cmd1.with_user(self.manager).write({"name": "Manager Updated Command"}) - except AccessError: - self.fail( - "Manager should be able to update the command " - "if in command.manager_ids." - ) - self.assertEqual(cmd1.with_user(self.manager).name, "Manager Updated Command") - - # Attempt to create a command as manager without including their ID - # in manager_ids should fail. - cmd_invalid_vals = { - "name": "Invalid Manager Create", - "access_level": "2", - "manager_ids": [(6, 0, [])], - "action": "python_code", - "code": "print('dummy')", - } - with self.assertRaises(AccessError): - self.Command.with_user(self.manager).create(cmd_invalid_vals) - - def test_manager_unlink_access(self): - """ - For a manager: - Allowed to delete a command if access_level <= "2", - the current user is the record creator, - AND the command's own manager_ids includes the manager. - """ - # Scenario 1: Command created by the manager with manager_ids - # including the manager. - cmd1 = self.Command.with_user(self.manager).create( - { - "name": "Manager Created Command", - "access_level": "2", - } - ) - try: - cmd1.unlink() - except AccessError: - self.fail( - "Manager should be able to delete a command " - "they created if in command.manager_ids." - ) - - # Scenario 2: Command created by someone else - # even if manager_ids includes the manager. - cmd2 = self._create_command( - **{ - "access_level": "2", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - with self.assertRaises(AccessError): - cmd2.with_user(self.manager).unlink() - - def test_root_unrestricted_access(self): - """ - For a root user: - Unlimited access: root can read, write, create, and delete commands - regardless of access_level or related servers. - """ - cmd = self._create_command( - **{ - "access_level": "3", # above the threshold for managers - } - ) - recs = self.Command.with_user(self.root).search([("id", "=", cmd.id)]) - self.assertIn( - cmd, - recs, - "Root should see the command regardless of restrictions.", - ) - try: - cmd.with_user(self.root).write({"name": "Root Updated Command"}) - except AccessError: - self.fail( - "Root should be able to update the command " "without restrictions." - ) - self.assertEqual(cmd.with_user(self.root).name, "Root Updated Command") - cmd2 = self.Command.with_user(self.root).create( - { - "name": "Root Created Command", - "access_level": "3", - "action": "python_code", - "code": "print('root')", - } - ) - self.assertTrue( - cmd2, - "Root should be able to create a command " "without restrictions.", - ) - cmd2.with_user(self.root).unlink() - recs_after = self.Command.with_user(self.root).search([("id", "=", cmd2.id)]) - self.assertFalse( - recs_after, - "Root should be able to delete the command without restrictions.", - ) - - def test_ssh_command_prepare_method_without_path(self): - """Test ssh command preparation in different modes without path""" - - server = self.server_test_1 - - single_command = "ls -a /tmp" - multiple_commands = "ls -a /tmp && mkdir /tmp/test" - - sudo_mode = "p" - - # Prepare single command for sudo with password - cmd = server._prepare_ssh_command(single_command, path=None, sudo=sudo_mode) - self.assertEqual( - cmd, - [f"{self.sudo_prefix} {single_command}"], - msg=( - "Single command for sudo with password should be " - "equal to list with the original command" - "as an only element" - ), - ) - - # Prepare multiple commands for sudo with password - cmd = server._prepare_ssh_command(multiple_commands, path=None, sudo=sudo_mode) - self.assertEqual( - cmd, - [ - f"{self.sudo_prefix} ls -a /tmp", - f"{self.sudo_prefix} mkdir /tmp/test", - ], - msg=( - "Multiple commands with sudo with password should be " - "a list of separated commands from original line" - ), - ) - - sudo_mode = "n" - - # Prepare single command for sudo without password - cmd = server._prepare_ssh_command(single_command, path=None, sudo=sudo_mode) - self.assertEqual( - cmd, - f"{self.sudo_prefix} {single_command}", - msg=( - "Single command with sudo without password should be " - f'equal to the original command prefixed with "{self.sudo_prefix}"' - ), - ) - - # Prepare multiple commands for sudo without password - cmd = server._prepare_ssh_command(multiple_commands, path=None, sudo=sudo_mode) - self.assertEqual( - cmd, - f"{self.sudo_prefix} ls -a /tmp && {self.sudo_prefix} mkdir /tmp/test", - msg=( - "Multiple commands with sudo with password should be " - "a re-joined string from list of separated original " - f'each prefixed with "{self.sudo_prefix}"' - ), - ) - - # Prepare single command without sudo - cmd = server._prepare_ssh_command(single_command) - self.assertEqual( - cmd, - single_command, - msg=( - "Single command without sudo should be " - "equal to the original command " - ), - ) - - # Prepare multiple without sudo - cmd = server._prepare_ssh_command(multiple_commands) - self.assertEqual( - cmd, - multiple_commands, - msg=( - "Multiple commands without sudo should be " - "equal to the original line of commands" - ), - ) - - def test_ssh_command_prepare_method_with_path(self): - """Test command preparation in different modes without path""" - - server = self.server_test_1 - - single_command = "ls -a /tmp" - multiple_commands = "ls -a /tmp && mkdir /tmp/test" - path = "/home/doge" - - sudo_mode = "p" - - # Prepare single command for sudo with password - cmd = server._prepare_ssh_command(single_command, path=path, sudo=sudo_mode) - self.assertEqual( - cmd, - [f"cd {path}", f"{self.sudo_prefix} {single_command}"], - msg=( - "Single command for sudo with password should be " - "equal to list of two elements:" - " change directory and original command" - ), - ) - - # Prepare multiple commands for sudo with password - cmd = server._prepare_ssh_command(multiple_commands, path=path, sudo=sudo_mode) - self.assertEqual( - cmd, - [ - f"cd {path}", - f"{self.sudo_prefix} ls -a /tmp", - f"{self.sudo_prefix} mkdir /tmp/test", - ], - msg=( - "Multiple commands with sudo with password should be " - "a list of separated commands from original line" - ), - ) - - sudo_mode = "n" - - # Prepare single command for sudo without password - cmd = server._prepare_ssh_command(single_command, path=path, sudo=sudo_mode) - self.assertEqual( - cmd, - f"cd {path} && {self.sudo_prefix} {single_command}", - msg=( - "Single command with sudo without password should be " - f'equal to the original command prefixed with "{self.sudo_prefix}"' - ), - ) - - # Prepare multiple commands for sudo without password - cmd = server._prepare_ssh_command(multiple_commands, path=path, sudo=sudo_mode) - self.assertEqual( - cmd, - f"cd {path} && {self.sudo_prefix} ls -a /tmp && {self.sudo_prefix} mkdir /tmp/test", # noqa - msg=( - "Multiple commands with sudo with password should be " - "a re-joined string from list of separated original " - f'each prefixed with "{self.sudo_prefix}"' - ), - ) - - # Prepare single command without sudo - cmd = server._prepare_ssh_command(single_command, path=path) - self.assertEqual( - cmd, - f"cd {path} && {single_command}", - msg=( - "Single command for without sudo should be " - "equal to the the original command" - "with 'cd {{ path }} && ' prefix" - ), - ) - - # Prepare multiple commands without sudo - cmd = server._prepare_ssh_command(multiple_commands, path=path) - self.assertEqual( - cmd, - f"cd {path} && {multiple_commands}", # noqa - msg=( - "Multiple commands without sudo should be " - "original command with 'change directory' command prepended" - ), - ) - - def test_ssh_command_no_split_for_sudo_without_path(self): - """If no_split_for_sudo=True, even '&&' shouldn’t split into a list.""" - server = self.server_test_1 - cmd_line = "echo a && echo b" - sudo_mode = "p" - result = server._prepare_ssh_command( - cmd_line, sudo=sudo_mode, no_split_for_sudo=True - ) - expected = [f"{self.sudo_prefix} {cmd_line}"] - self.assertEqual( - result, expected, "With no_split_for_sudo, '&&' must not produce a list" - ) - - def test_ssh_command_no_split_for_sudo_with_path(self): - """Same, but with a custom cwd prefix.""" - server = self.server_test_1 - cmd_line = "echo a && echo b" - path = "/tmp" - sudo_mode = "p" - result = server._prepare_ssh_command( - cmd_line, path=path, sudo=sudo_mode, no_split_for_sudo=True - ) - expected = [f"cd {path}", f"{self.sudo_prefix} {cmd_line}"] - self.assertEqual( - result, - expected, - "With no_split_for_sudo and path, the entire '&&' string remains un-split", - ) - - def test_server_render_command(self): - """Test rendering command using `_render_command` method - of cx.tower.server - """ - - # -- 1 -- - # Test with default path - rendered_command = self.server_test_1._render_command(self.command_create_dir) - rendered_code_expected = "cd /opt/tower && mkdir test-odoo-1" - rendered_path_expected = f"/home/{self.server_test_1.ssh_username}" - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - self.assertEqual( - rendered_command["rendered_path"], - rendered_path_expected, - "Rendered path doesn't match", - ) - - # -- 2 -- - # Test with custom path - rendered_command = self.server_test_1._render_command( - self.command_create_dir, path="/such/much/path" - ) - rendered_code_expected = "cd /opt/tower && mkdir test-odoo-1" - rendered_path_expected = "/such/much/path" - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - self.assertEqual( - rendered_command["rendered_path"], - rendered_path_expected, - "Rendered path doesn't match", - ) - - # -- 3 -- - # Set variable_path to None and check again - variable_value_path = self.server_test_1.variable_value_ids.filtered( - lambda var_val: var_val.variable_id.id == self.variable_path.id - ) - variable_value_path.value_char = None - rendered_command = self.server_test_1._render_command(self.command_create_dir) - rendered_code_expected = "cd False && mkdir test-odoo-1" - rendered_path_expected = f"/home/{self.server_test_1.ssh_username}" - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - self.assertEqual( - rendered_command["rendered_path"], - rendered_path_expected, - "Rendered path doesn't match", - ) - - # -- 4 -- - # Set both path and code to None - self.write_and_invalidate( - self.command_create_dir, **{"code": None, "path": None} - ) - rendered_command = self.server_test_1._render_command(self.command_create_dir) - - self.assertFalse( - rendered_command["rendered_code"], "Rendered code doesn't match" - ) - self.assertFalse( - rendered_command["rendered_path"], "Rendered path doesn't match" - ) - - def test_server_render_command_with_custom_variable_values(self): - """Test rendering command using `_render_command` method - of cx.tower.server with custom variable values - """ - self.write_and_invalidate( - self.server_test_1, - **{"user_ids": [(4, self.user.id)], "manager_ids": [(4, self.manager.id)]}, - ) - # -- 1 -- - # Set custom variable values - custom_variable_values = { - "test_path_": "/pepe/memes", - "other_path": "/etc/chad", - } - - # Modify command path - self.write_and_invalidate( - self.command_create_dir, - **{"path": "{{ other_path }}/{{ tower.server.username }}"}, - ) - - # Render command - rendered_command = self.server_test_1.with_user(self.manager)._render_command( - self.command_create_dir, custom_variable_values=custom_variable_values - ) - rendered_code_expected = "cd /pepe/memes && mkdir test-odoo-1" - rendered_path_expected = f"/etc/chad/{self.server_test_1.ssh_username}" - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - self.assertEqual( - rendered_command["rendered_path"], - rendered_path_expected, - "Rendered path doesn't match", - ) - - # -- 2 -- - # Test with user who doesn't have access to the server - rendered_command = self.server_test_1.with_user(self.user)._render_command( - self.command_create_dir, custom_variable_values=custom_variable_values - ) - rendered_code_expected = "cd /opt/tower && mkdir test-odoo-1" - rendered_path_expected = f"None/{self.server_test_1.ssh_username}" - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - self.assertEqual( - rendered_command["rendered_path"], - rendered_path_expected, - "Rendered path doesn't match", - ) - - def test_server_render_command_variable_with_value_modifier(self): - """Test rendering command using `_render_command` method - of cx.tower.server. - Use variable with value modifier for testing. - """ - - # -- 1 -- - # Set modifiers for variables - modifier_for_path = """ -if 'opt' in value: - result = value.replace('opt', 'home') -else: - result = value -""" - self.variable_path.applied_expression = modifier_for_path - - modifier_for_dir = """ -pattern = r'(?i)odoo' -replacement = 'sap' -result = re.sub(pattern, replacement, value) -""" - self.variable_dir.applied_expression = modifier_for_dir - - # -- 1 -- - # Test with default path - rendered_command = self.server_test_1._render_command(self.command_create_dir) - rendered_code_expected = "cd /home/tower && mkdir test-sap-1" - rendered_path_expected = f"/home/{self.server_test_1.ssh_username}" - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - self.assertEqual( - rendered_command["rendered_path"], - rendered_path_expected, - "Rendered path doesn't match", - ) - - # -- 2 -- - # Set invalid expression modifier - self.variable_path.applied_expression = "invalid" - with mute_logger( - "odoo.addons.cetmix_tower_server.models.cx_tower_variable_mixin" - ): - rendered_command = self.server_test_1._render_command( - self.command_create_dir - ) - rendered_code_expected = "cd /opt/tower && mkdir test-sap-1" - rendered_path_expected = f"/home/{self.server_test_1.ssh_username}" - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - self.assertEqual( - rendered_command["rendered_path"], - rendered_path_expected, - "Rendered path doesn't match", - ) - - # -- 3 -- - # Test with variable in variable value - complex_variable = self.Variable.create( - { - "name": "Complex Variable", - "applied_expression": "result = value.replace('opt', 'meme')", - } - ) - # Create a complex variable value - self.VariableValue.create( - { - "variable_id": complex_variable.id, - "value_char": "{{ test_path_ }}/{{ test_dir }}", - } - ) - command_with_complex_variable = self.Command.create( - { - "name": "Command with complex variable", - "code": "cd {{ complex_variable }}", - "action": "ssh_command", - } - ) - with mute_logger( - "odoo.addons.cetmix_tower_server.models.cx_tower_variable_mixin" - ): - rendered_command = self.server_test_1._render_command( - command_with_complex_variable - ) - rendered_code_expected = "cd /meme/tower/test-sap-1" - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - - # -- 4 -- - # Remove modifier from variable "Path" and check again - self.variable_dir.applied_expression = None - with mute_logger( - "odoo.addons.cetmix_tower_server.models.cx_tower_variable_mixin" - ): - rendered_command = self.server_test_1._render_command( - command_with_complex_variable - ) - rendered_code_expected = "cd /meme/tower/test-odoo-1" - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_expected, - "Rendered code doesn't match", - ) - - def test_render_code_generic(self): - """Test generic (aka ssh) code template direct rendering""" - - # Only 'test_path_' must be rendered - args = {"test_path_": "/tmp", "test_os": "debian"} - res = self.command_create_dir.render_code(**args) - rendered_code = res.get(self.command_create_dir.id) - rendered_code_expected = "cd /tmp && mkdir " - self.assertEqual( - rendered_code, - rendered_code_expected, - msg=f"Must be rendered as '{rendered_code_expected}'", - ) - - # 'test_path_' and 'dir' must be rendered - args = {"test_path_": "/tmp", "os": "debian", "test_dir": "odoo"} - res = self.command_create_dir.render_code(**args) - rendered_code = res.get(self.command_create_dir.id) - self.assertEqual( - rendered_code, - "cd /tmp && mkdir odoo", - msg="Must be rendered as 'cd /tmp && mkdir odoo'", - ) - - def test_run_command_with_variables(self): - """Test code execution using command log records""" - - x = 1 # Used to distinguish labels - - # Check with all available "sudo" option - for sudo in [False, "n", "p"]: - # Add label to track command log - self.server_test_1.use_sudo = sudo - command_label = f"Test Command {x}" - custom_values = {"log": {"label": command_label}} - - # Run command for Server 1 - self.server_test_1.run_command( - self.command_create_dir, sudo=sudo, **custom_values - ) - - # Expected rendered command code - rendered_code_expected = "cd /opt/tower && mkdir test-odoo-1" - - # Get command log - log_record = self.CommandLog.search([("label", "=", command_label)]) - - # Check log values - self.assertEqual(len(log_record), 1, msg="Must be a single log record") - self.assertEqual( - log_record.server_id.id, - self.server_test_1.id, - msg="Record must belong to Test 1", - ) - self.assertEqual( - log_record.command_id.id, - self.command_create_dir.id, - msg="Record must belong to command 'Create dir'", - ) - self.assertEqual( - log_record.code, - rendered_code_expected, - msg=f"Rendered code must be '{rendered_code_expected}'", - ) - self.assertEqual( - log_record.command_status, 0, msg="Command status must be equal to 0" - ) - self.assertEqual( - log_record.use_sudo, - sudo, - msg="'sudo' param in log doesn't match the command one", - ) - - # Increment label counter - x += 1 - - def test_run_command_with_keys(self): - """Test command with keys in code""" - - # Command - code = "cd {{ test_path_ }} && mkdir #!cxtower.secret.FOLDER!#" - command_with_keys = self.Command.create( - {"name": "Command with keys", "code": code} - ) - - # Parse command with key parser to ensure key is parsed correctly - code_parsed_expected = "cd {{ test_path_ }} && mkdir secretFolder" - code_parsed = self.Key._parse_code(code) - self.assertEqual( - code_parsed, - code_parsed_expected, - msg="Parsed code doesn't match expected one", - ) - - # Add label to track command log - command_label = "Test Command with keys" - custom_values = {"log": {"label": command_label}} - - # Run command for Server 1 - self.server_test_1.run_command(command_with_keys, **custom_values) - - # Expected rendered command code - rendered_code_expected = "cd /opt/tower && mkdir #!cxtower.secret.FOLDER!#" - - # Get command log - log_record = self.CommandLog.search([("label", "=", command_label)]) - - # Check log values - self.assertEqual(len(log_record), 1, msg="Must be a single log record") - self.assertEqual( - log_record.server_id.id, - self.server_test_1.id, - msg=("Record must belong %s", self.server_test_1.name), - ) - self.assertEqual( - log_record.command_id.id, - command_with_keys.id, - msg=("Record must belong to command %s", command_with_keys.name), - ) - self.assertEqual( - log_record.code, - rendered_code_expected, - msg=f"Rendered code must be '{rendered_code_expected}'", - ) - self.assertEqual( - log_record.command_status, 0, msg="Command status must be equal to 0" - ) - - def test_parse_ssh_command_result(self): - """Test ssh command result parsing""" - - placeholder = self.Key.SECRET_VALUE_PLACEHOLDER - # ------------------------------------------------------- - # Case 1: regular command execution result with no error - # We are testing secret value placeholder here - # ------------------------------------------------------- - status = 0 - response = ["Such much", f"Doge like SSH {placeholder}"] - error = [] - - ssh_command_result = self.Server._parse_command_results( - status, response, error, key_values=[f"{self.secret_2.secret_value}"] - ) - - # Get result - result_status = ssh_command_result["status"] - result_response = ssh_command_result["response"] - result_error = ssh_command_result["error"] - - self.assertEqual( - result_status, - result_status, - "Status in result must be the same as the initial one", - ) - self.assertEqual( - result_response, - f"Such muchDoge like SSH {placeholder}", - "Response in result doesn't match expected", - ) - self.assertIsNone(result_error, "Error in response must be set to None") - - # ------------------------------------------------------- - # Case 2: no response but an error - # ------------------------------------------------------- - status = 1 - response = [] - error = ["Ooops", "I did", "it again"] - - ssh_command_result = self.Server._parse_command_results(status, response, error) - - # Get result - result_status = ssh_command_result["status"] - result_response = ssh_command_result["response"] - result_error = ssh_command_result["error"] - - self.assertEqual( - result_status, - result_status, - "Status in result must be the same as the initial one", - ) - self.assertIsNone(result_response, "Response in response must be set to None") - self.assertEqual( - result_error, "OoopsI didit again", "Error in result doesn't match expected" - ) - - # ------------------------------------------------------- - # Case 3: several codes all 0, no response but an error - # ------------------------------------------------------- - status = [0, 0, 0] - response = [] - error = ["Ooops", "I did", "it again"] - - ssh_command_result = self.Server._parse_command_results(status, response, error) - - # Get result - result_status = ssh_command_result["status"] - result_response = ssh_command_result["response"] - result_error = ssh_command_result["error"] - - self.assertEqual( - result_status, 0, "Status in result doesn't match expected one" - ) - self.assertIsNone(result_response, "Response in response must be set to None") - self.assertEqual( - result_error, "OoopsI didit again", "Error in result doesn't match expected" - ) - - # ------------------------------------------------------- - # Case 4: codes [0,1,0,4,0], no response but an error - # ------------------------------------------------------- - status = [0, 1, 0, 4, 0] - response = [] - error = ["Ooops", "I did", "it again"] - - ssh_command_result = self.Server._parse_command_results(status, response, error) - - # Get result - result_status = ssh_command_result["status"] - result_response = ssh_command_result["response"] - result_error = ssh_command_result["error"] - - self.assertEqual( - result_status, 4, "Status in result doesn't match expected one" - ) - self.assertIsNone(result_response, "Response in response must be set to None") - self.assertEqual( - result_error, "OoopsI didit again", "Error in result doesn't match expected" - ) - - # ------------------------------------------------------- - # Case 5: regular command execution result with no error - # However the command result is saved in the "error" value. - # For example this happens in 'docker build'. - # ------------------------------------------------------- - status = 0 - error = ["Such much", f"Doge like SSH {placeholder}"] - response = [] - - ssh_command_result = self.Server._parse_command_results( - status, response, error, key_values=[f"{self.secret_2.secret_value}"] - ) - - # Get result - result_status = ssh_command_result["status"] - result_response = ssh_command_result["response"] - result_error = ssh_command_result["error"] - - self.assertEqual( - result_status, - result_status, - "Status in result must be the same as the initial one", - ) - self.assertEqual( - result_error, - f"Such muchDoge like SSH {placeholder}", - "Response in result doesn't match expected", - ) - self.assertIsNone(result_response, "Error in response must be set to None") - - def test_tower_command_action_file_using_template(self): - """ - Test action file using template for tower source - """ - with patch( - "odoo.addons.cetmix_tower_server.models.cx_tower_server.CxTowerServer.upload_file", - return_value="ok", - ): - self.server_test_1.run_command( - self.command_create_file_with_template_tower_source - ) - - log_text_create_success = "File created and uploaded successfully" - log_text_file_exists = "An error occurred: File already exists on server." - - # Get command log - log_record = self.CommandLog.search( - [ - ("server_id", "=", self.server_test_1.id), - ( - "command_id", - "=", - self.command_create_file_with_template_tower_source.id, - ), - ("command_response", "=", log_text_create_success), - ] - ) - - self.assertEqual(len(log_record), 1, msg="Must be a single log record") - - with patch( - "odoo.addons.cetmix_tower_server.models.cx_tower_server.CxTowerServer.upload_file", - return_value="ok", - ): - self.server_test_1.run_command( - self.command_create_file_with_template_tower_source - ) - - log_record_2 = self.CommandLog.search( - [ - ("server_id", "=", self.server_test_1.id), - ( - "command_id", - "=", - self.command_create_file_with_template_tower_source.id, - ), - ("command_error", "=", log_text_file_exists), - ] - ) - - self.assertEqual(len(log_record_2), 1, msg="Must be a single log record") - - def test_server_command_action_file_using_template(self): - """ - Test action file using template for server source - """ - self.assertFalse(self.template_file_server.file_ids) - - def download_file(this, remote_path): - return b"Hello, world!" - - cx_tower_server_obj = self.registry["cx.tower.server"] - - with patch.object(cx_tower_server_obj, "download_file", download_file): - self.server_test_1.run_command( - self.command_create_file_with_template_server_source - ) - - log_text_create_success = "File created and uploaded successfully" - log_text_file_exists = "An error occurred: File already exists on server." - - # Get command log - log_record = self.CommandLog.search( - [ - ("server_id", "=", self.server_test_1.id), - ( - "command_id", - "=", - self.command_create_file_with_template_server_source.id, - ), - ("command_response", "=", log_text_create_success), - ] - ) - - self.assertEqual(len(log_record), 1, msg="Must be a single log record") - self.assertEqual( - len(self.template_file_server.file_ids), 1, msg="Must be one file!" - ) - self.assertEqual( - self.template_file_server.file_ids.source, - "server", - msg="The File source must be 'server'", - ) - - with patch.object(cx_tower_server_obj, "download_file", download_file): - self.server_test_1.run_command( - self.command_create_file_with_template_server_source - ) - - log_record_2 = self.CommandLog.search( - [ - ("server_id", "=", self.server_test_1.id), - ( - "command_id", - "=", - self.command_create_file_with_template_server_source.id, - ), - ("command_error", "=", log_text_file_exists), - ] - ) - - self.assertEqual(len(log_record_2), 1, msg="Must be a single log record") - - def test_run_command_no_command_log(self): - """Run command without creating a log record. - Such commands return execution result directly. - """ - # Add label to track command log - command_label = "Test Command with keys" - custom_values = {"log": {"label": command_label}} - - # Run command for Server 1 - command_result = self.server_test_1.with_context( - no_command_log=True - ).run_command(self.command_create_dir, **custom_values) - self.assertEqual( - command_result["status"], 0, "Command status doesn't match expected one" - ) - self.assertEqual( - command_result["response"], - "ok", - "Command response doesn't match expected one", - ) - self.assertIsNone( - command_result["error"], "Command error doesn't match expected one" - ) - - def test_another_command_is_running(self): - """Test a case when another command is running on the same server""" - - # Remove all existing command logs - self.CommandLog.search([]).unlink() - - # Create a new command log - initial_command_log = self.CommandLog.create( - { - "server_id": self.server_test_1.id, - "command_id": self.command_create_new_command.id, - "start_date": Datetime.now(), - } - ) - - # Run the command without creating a log record - command_result = self.server_test_1.with_context( - no_command_log=True - ).run_command(self.command_create_new_command) - self.assertEqual(command_result["status"], ANOTHER_COMMAND_RUNNING) - - # Run the command with creating a log record - command_result = self.server_test_1.run_command(self.command_create_new_command) - - # Get the command log - command_log = self.CommandLog.search( - [ - ("server_id", "=", self.server_test_1.id), - ("command_id", "=", self.command_create_new_command.id), - ("id", "!=", initial_command_log.id), - ] - ) - self.assertEqual(len(command_log), 1, "Must be a single log record") - self.assertEqual(command_log.command_status, ANOTHER_COMMAND_RUNNING) - - def test_file_using_template_create_if_exists(self): - """Test uploading file using template if it exists on server.""" - - command = self.command_create_file_with_template_server_source - command.write({"if_file_exists": "skip"}) - - # Create file to make sure that it exists on the server - file_template = command.file_template_id - orig_file = file_template.create_file( - server=self.server_test_1, - server_dir=file_template.server_dir, - if_file_exists=command.if_file_exists, - ) - - self.assertTrue(orig_file, "File must be created on the server") - - # Test if file exists and command is set to "skip" - skipped_file = file_template.create_file( - server=self.server_test_1, - server_dir=file_template.server_dir, - if_file_exists=command.if_file_exists, - ) - self.assertEqual( - orig_file, - skipped_file, - "Skip should return the existing file, not create a new one", - ) - self.assertEqual( - self.env["cx.tower.file"].search_count( - [ - ("template_id", "=", file_template.id), - ("server_id", "=", self.server_test_1.id), - ] - ), - 1, - "There must be exactly one physical file record after skip", - ) - - # Change command to raise an error if file exists - command.write({"if_file_exists": "raise"}) - with self.assertRaisesRegex( - ValidationError, - "File already exists on server.", - ): - file_template.create_file( - server=self.server_test_1, - server_dir=file_template.server_dir, - if_file_exists=command.if_file_exists, - ) - # Change command to "overwrite" file if it exists - command.write({"if_file_exists": "overwrite"}) - # Run command again, it should overwrite the file - file_template.create_file( - server=self.server_test_1, - server_dir=file_template.server_dir, - if_file_exists=command.if_file_exists, - ) - self.assertEqual( - self.env["cx.tower.file"].search_count( - [ - ("template_id", "=", file_template.id), - ("server_id", "=", self.server_test_1.id), - ("server_dir", "=", file_template.server_dir), - ] - ), - 1, - "There must be exactly one physical file record after overwrite", - ) - self.assertEqual( - orig_file.code, - file_template.code, - "File code must match template after overwrite", - ) - self.assertEqual( - orig_file.name, - file_template.file_name, - "File name must match template after overwrite", - ) - self.assertEqual( - orig_file.source, - file_template.source, - "File source must match template after overwrite", - ) - - def test_is_file_disconnected_from_template(self): - """Test if file is disconnected from template after being created.""" - - initial_files = self.server_test_1.file_ids - command = self.command_create_file_with_template_server_source - - command.disconnect_file = True - self.server_test_1.run_command(command=command) - - new_files = self.server_test_1.file_ids - initial_files - self.assertEqual(len(new_files), 1, "Must be one new file created") - self.assertEqual( - new_files.code_on_server, - command.file_template_id.code, - "File code must match template", - ) - self.assertFalse( - new_files.template_id, "File must be disconnected from template" - ) - - # --------------------- - # ********************* - # Python commands - # ********************* - # --------------------- - - def test_render_code_python(self): - """Test Python code template direct rendering""" - - rendered_command = self.server_test_1._render_command( - self.command_create_new_command - ) - - # Note: this is rendered as for Server Test 1 - rendered_code_pythonic = ( - f""" -server_name = "{self.server_test_1.name}" -if server_name and #!cxtower.secret.FOLDER!# == "secretFolder": - # We don't actually create a new command because it will raise - # access error if user doesn't have access to 'create' operation. - # Instead we just return a dummy command result. - command = "new command" - result = {{"exit_code": 0, "message": "New command was created"}} -else: - result = {{"exit_code": %s, "message": "error"}} - """ - % GENERAL_ERROR - ) - - self.assertEqual( - rendered_command["rendered_code"], - rendered_code_pythonic, - "Rendered code doesn't match", - ) - - def test_execute_python_command(self): - """ - Run command with python action. - """ - command_result = self.server_test_1.with_context( - no_command_log=True - ).run_command(self.command_create_new_command) - self.assertEqual( - command_result["status"], 0, "The command result status must be 0" - ) - self.assertEqual( - command_result["response"], - "New command was created", - "The response must be text", - ) - - # Check error is raises - self.secret_folder_key.secret_value = "not_a_secretFolder" - command_result = self.server_test_1.with_context( - no_command_log=True - ).run_command(self.command_create_new_command) - self.assertEqual( - command_result["status"], - GENERAL_ERROR, - "The command result status must be GENERAL_ERROR", - ) - self.assertEqual( - command_result["error"], - "error", - "The error response must be contain text - error", - ) - - def test_run_python_code(self): - """ - Test python execution code - """ - rendered_command = self.server_test_1._render_command( - self.command_create_new_command - ) - - command_result = self.server_test_1._run_python_code( - rendered_command["rendered_code"] - ) - self.assertEqual( - command_result["status"], 0, "The command result status must be 0" - ) - self.assertEqual( - command_result["response"], - "New command was created", - "The response must be text", - ) - self.assertIsNone( - command_result["error"], - "Error in command result must be set to None", - ) - - def test_run_command_without_set_server_status(self): - """ - Test command execution without setting server status - """ - # Set command access level to "user" - self.command_create_new_command.write({"access_level": "1"}) - - # Add user to command - self.write_and_invalidate( - self.server_test_1, **{"user_ids": [(4, self.user.id)]} - ) - - # Reset access rule cache - self.env["ir.rule"].invalidate_recordset() - - # Run command - server_status = self.server_test_1.status - - result = ( - self.server_test_1.with_context(no_command_log=True) - .with_user(self.user) - .run_command(self.command_create_new_command) - ) - - # Check command result - self.assertEqual(result["status"], 0, "Command status must be 0") - self.assertEqual( - self.server_test_1.status, server_status, "Server status must be 'running'" - ) - - def test_run_command_with_set_server_status(self): - """ - Test command execution with setting server status - """ - # Set server status to "down" - self.command_create_new_command.write({"server_status": "stopping"}) - - # Run command - self.server_test_1.with_context(no_command_log=True).run_command( - self.command_create_new_command - ) - - # Check command result - self.assertEqual( - self.server_test_1.status, "stopping", "Server status must be 'stopping'" - ) - - def test_run_python_code_with_secret(self): - """ - Test execution of Python code with a secret value. - This test ensures that a command is rendered and executed correctly, - and that the secret value is correctly handled and replaced in the output. - """ - - placeholder = self.Key.SECRET_VALUE_PLACEHOLDER - # Case 1 - # Render the command using server_test_1 - rendered_command = self.server_test_1._render_command( - self.command_python_command_1 - ) - - # Run the rendered Python code - command_result = self.server_test_1._run_python_code( - rendered_command["rendered_code"] - ) - - # Assert that the command execution status is 0 (indicating success) - self.assertEqual( - command_result["status"], 0, "The command result status must be 0" - ) - - # Assert that the response contains the secret spoiler text - self.assertEqual( - command_result["response"], - placeholder, - "The response must correctly include the secret value placeholder", - ) - - # Assert that no error occurred during execution (error should be None) - self.assertIsNone( - command_result["error"], - "The error in command result must be None", - ) - - # Case 2 - # Render the command using server_test_1 - rendered_command = self.server_test_1._render_command( - self.command_python_command_2 - ) - - # Run the rendered Python code - command_result = self.server_test_1._run_python_code( - rendered_command["rendered_code"] - ) - - # Assert that the command execution status is 0 (indicating success) - self.assertEqual( - command_result["status"], 0, "The command result status must be 0" - ) - - # Assert that the response contains the secret spoiler text - self.assertEqual( - command_result["response"], - f'We use "{placeholder}"', - "The response must correctly include the secret value placeholder", - ) - - # Assert that no error occurred during execution (error should be None) - self.assertIsNone( - command_result["error"], - "The error in command result must be None", - ) - - # Case 3 - # Render the command using server_test_1 - rendered_command = self.server_test_1._render_command( - self.command_python_command_3 - ) - - # Run the rendered Python code - command_result = self.server_test_1._run_python_code( - rendered_command["rendered_code"] - ) - - # Assert that the command execution status is 0 (indicating success) - self.assertEqual( - command_result["status"], 0, "The command result status must be 0" - ) - - # Assert that the response contains the secret spoiler text - self.assertEqual( - command_result["response"], - placeholder, - "The response must correctly include the secret value placeholder", - ) - - # Assert that no error occurred during execution (error should be None) - self.assertIsNone( - command_result["error"], - "The error in command result must be None", - ) - - # Case 4 - # Render the command using server_test_1 - rendered_command = self.server_test_1._render_command( - self.command_python_command_4 - ) - - # Run the rendered Python code - # SSH keys are not parsed inline, so we should raise a validation error - command_result = self.server_test_1._run_python_code( - rendered_command["rendered_code"] - ) - - # Assert that the command execution status is 0 (indicating success) - self.assertEqual( - command_result["status"], 0, "The command result status must be 0" - ) - - # Assert that the response contains the secret spoiler text - self.assertEqual( - command_result["response"], - placeholder, - "The response must correctly include the secret value placeholder", - ) - - # Assert that no error occurred during execution (error should be None) - self.assertIsNone( - command_result["error"], - "The error in command result must be None", - ) - - def test_command_with_secret(self): - """ - Test case to verify that when a command includes a secret reference, - the secret key is automatically linked with the command. - """ - - # Command with a secret reference - code = "cd {{ test_path_ }} && mkdir #!cxtower.secret.FOLDER!#" - - secrets = self.Command._extract_secret_ids(code) - secret_folder_key = self.secret_folder_key - self.assertIn( - secret_folder_key, - secrets, - msg=( - f"The expected secret ID #{secret_folder_key.id} " - "was not found in the provided code." - ), - ) - - command_with_keys = self.Command.create( - {"name": "Command with keys", "code": code} - ) - - # -- 1 -- - # Assert that the secret key is linked with the command - self.assertIn( - secret_folder_key, - command_with_keys.secret_ids, - msg="The secret key is not linked with the command.", - ) - - # -- 2 -- - # Update the command's code to remove the secret reference - updated_code = "cd {{ test_path_ }} && mkdir new_folder" - command_with_keys.code = updated_code - - self.assertFalse( - command_with_keys.secret_ids, - msg=( - "The secret_ids field should be empty after " - "removing the secret reference from command." - ), - ) - - # -- 3 -- - # Create a secret with the same reference but connected to another server - another_server = self.server_test_1.copy({"name": "another server"}) - another_secret = self.Key.create( - { - "name": "another secret", - "reference": secret_folder_key.reference, - "key_type": "s", - } - ) - another_secret_value = self.KeyValue.create( - { - "key_id": another_secret.id, - "server_id": another_server.id, - "secret_value": "another secret value", - } - ) - # Set original code again - command_with_keys.code = code - self.assertEqual( - len(command_with_keys.secret_ids), - 1, - msg="Must be only one secret", - ) - self.assertIn( - secret_folder_key, - command_with_keys.secret_ids, - msg="The secret key is not linked with the command.", - ) - self.assertNotIn( - another_secret, - command_with_keys.secret_ids, - msg="The another secret is linked with the command.", - ) - - # -- 4 -- - # Connect command to server and secret to another server - # and ensure it's unlinked from the command. - yet_one_more_server = self.server_test_1.copy({"name": "yet one more server"}) - - self.write_and_invalidate( - another_secret_value, **{"server_id": yet_one_more_server.id} - ) - self.write_and_invalidate( - command_with_keys, **{"server_ids": self.server_test_1} - ) - self.assertEqual( - len(command_with_keys.secret_ids), - 1, - msg="Must be one secret", - ) - - def test_check_zombie_commands(self): - """Test checking and marking zombie commands""" - # Create test commands - ssh_command = self.Command.create( - { - "name": "Test SSH Command", - "code": "ls -la", - "action": "ssh_command", - } - ) - python_command = self.Command.create( - { - "name": "Test Python Command", - "code": "print('test')", - "action": "python_code", - } - ) - plan_command = self.Command.create( - { - "name": "Test Plan Command", - "code": "test plan", - "action": "plan", - } - ) - - # Set command timeout to 10 seconds - self.env["ir.config_parameter"].sudo().set_param( - "cetmix_tower_server.command_timeout", "10" - ) - - # Create command logs with different start times - now = Datetime.now() - old_time = now - timedelta(seconds=20) # Older than timeout - recent_time = now - timedelta(seconds=5) # Within timeout - - # Create zombie SSH command log - zombie_ssh_log = self.CommandLog.create( - { - "command_id": ssh_command.id, - "server_id": self.server_test_1.id, - "start_date": old_time, - } - ) - - # Create zombie Python command log - zombie_python_log = self.CommandLog.create( - { - "command_id": python_command.id, - "server_id": self.server_test_1.id, - "start_date": old_time, - } - ) - - # Create non-zombie command logs - active_ssh_log = self.CommandLog.create( - { - "command_id": ssh_command.id, - "server_id": self.server_test_1.id, - "start_date": recent_time, - } - ) - - plan_log = self.CommandLog.create( - { - "command_id": plan_command.id, - "server_id": self.server_test_1.id, - "start_date": old_time, - } - ) - - # Test with timeout set - self.server_test_1._check_zombie_commands() - - # Check zombie commands are marked as finished - self.assertFalse( - zombie_ssh_log.is_running, "Zombie SSH command should be marked as finished" - ) - self.assertFalse( - zombie_python_log.is_running, - "Zombie Python command should be marked as finished", - ) - self.assertEqual( - zombie_ssh_log.command_status, - COMMAND_TIMED_OUT, - "Zombie SSH command should have timed out status", - ) - self.assertEqual( - zombie_python_log.command_error, - COMMAND_TIMED_OUT_MESSAGE, - "Zombie Python command should have timeout error message", - ) - - # Check non-zombie commands are still running - self.assertTrue( - active_ssh_log.is_running, "Recent command should still be running" - ) - self.assertTrue( - plan_log.is_running, "Plan command should not be affected by timeout" - ) - - # Test with timeout disabled - self.env["ir.config_parameter"].sudo().set_param( - "cetmix_tower_server.command_timeout", "0" - ) - - # Create new zombie command log - new_zombie_log = self.CommandLog.create( - { - "command_id": ssh_command.id, - "server_id": self.server_test_1.id, - "start_date": old_time, - } - ) - - self.server_test_1._check_zombie_commands() - self.assertNotEqual( - new_zombie_log.command_status, - COMMAND_TIMED_OUT, - "Commands should not be marked as timed out when timeout is disabled", - ) - - def test_command_with_malformed_code(self): - """Test rendering command using `_render_command` method - of cx.tower.server with malformed code - """ - - with self.assertRaises(ValidationError): - self.Command.create( - { - "name": "Test Malformed Command", - "code": "cd {{ !@238203 }} && mkdir #!cxtower.secret.FOLDER!#", - "action": "ssh_command", - } - ) diff --git a/addons/cetmix_tower_server/tests/test_command_log.py b/addons/cetmix_tower_server/tests/test_command_log.py deleted file mode 100644 index 9c1ed86..0000000 --- a/addons/cetmix_tower_server/tests/test_command_log.py +++ /dev/null @@ -1,282 +0,0 @@ -# Copyright (C) 2025 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import fields -from odoo.exceptions import AccessError - -from .common import TestTowerCommon - - -class TestTowerCommandLog(TestTowerCommon): - """Test the cx.tower.command.log model access rights.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create commands with different access levels - cls.command_level_1 = cls.Command.create( - { - "name": "Test Command L1", - "action": "ssh_command", - "access_level": "1", - } - ) - - cls.command_level_2 = cls.Command.create( - { - "name": "Test Command L2", - "action": "ssh_command", - "access_level": "2", - } - ) - - cls.command_level_3 = cls.Command.create( - { - "name": "Test Command L3", - "action": "ssh_command", - "access_level": "3", - } - ) - - # Create test command logs with specific users - cls.command_log_1 = ( - cls.CommandLog.with_user(cls.user) - .sudo() - .create( - { - "server_id": cls.server_test_1.id, - "command_id": cls.command_level_1.id, - "start_date": fields.Datetime.now(), - } - ) - ) - - cls.command_log_2 = ( - cls.CommandLog.with_user(cls.manager) - .sudo() - .create( - { - "server_id": cls.server_test_1.id, - "command_id": cls.command_level_1.id, - "start_date": fields.Datetime.now(), - } - ) - ) - - # Create additional server for testing - cls.server_2 = cls.Server.create( - { - "name": "Test Server 2", - "ip_v4_address": "localhost", - "ssh_username": "test2", - "ssh_password": "test2", - "ssh_port": 22, - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - } - ) - - def test_user_read_access(self): - """Test user read access to command logs""" - # Add user to server's user_ids to isolate creator check - self.server_test_1.write( - { - "user_ids": [(6, 0, [self.user.id])], - } - ) - - # Case 1: User should be able to read when: - # - access_level == "1" - # - created by user - # - user is in server's user_ids - recs = self.CommandLog.with_user(self.user).search( - [("id", "in", [self.command_log_1.id, self.command_log_2.id])] - ) - self.assertEqual( - len(recs), - 1, - "User should only be able to read their own logs", - ) - self.assertIn( - self.command_log_1, - recs, - "User should be able to read own logs when conditions are met", - ) - self.assertNotIn( - self.command_log_2, - recs, - "User should not be able to read logs created by others", - ) - - # Case 2: User should not be able to read when not in server's user_ids - self.server_test_1.write( - { - "user_ids": [(5, 0, 0)], # Remove all users - } - ) - recs = self.CommandLog.with_user(self.user).search( - [("id", "=", self.command_log_1.id)] - ) - self.assertNotIn( - self.command_log_1, - recs, - "User should not be able to read when not in server's user_ids", - ) - - # Case 3: User should not be able to read when access_level > "1" - self.server_test_1.write( - { - "user_ids": [(6, 0, [self.user.id])], - } - ) - high_access_log = ( - self.CommandLog.with_user(self.user) - .sudo() - .create( - { - "server_id": self.server_test_1.id, - "command_id": self.command_level_2.id, # Using command with access_level "2" # noqa: E501 - "start_date": fields.Datetime.now(), - } - ) - ) - recs = self.CommandLog.with_user(self.user).search( - [("id", "=", high_access_log.id)] - ) - self.assertNotIn( - high_access_log, - recs, - "User should not be able to read logs with access_level > '1'" - " even if created by them", - ) - - def test_manager_read_access(self): - """Test manager read access to command logs""" - # Case 1: Manager should be able to read when: - # - access_level <= "2" - # - manager is in server's manager_ids - self.server_test_1.write( - { - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - recs = self.CommandLog.with_user(self.manager).search( - [("id", "in", [self.command_log_1.id, self.command_log_2.id])] - ) - self.assertEqual( - len(recs), - 2, - "Manager should be able to read all logs when in server's manager_ids", - ) - - # Case 2: Manager should be able to read when in server's user_ids - self.server_test_1.write( - { - "manager_ids": [(5, 0, 0)], # Remove all managers - "user_ids": [(6, 0, [self.manager.id])], - } - ) - recs = self.CommandLog.with_user(self.manager).search( - [("id", "in", [self.command_log_1.id, self.command_log_2.id])] - ) - self.assertEqual( - len(recs), - 2, - "Manager should be able to read all logs when in server's user_ids", - ) - - # Case 3: Manager should not be able to read when access_level > "2" - high_access_log = ( - self.CommandLog.with_user(self.manager) - .sudo() - .create( - { - "server_id": self.server_test_1.id, - "command_id": self.command_level_3.id, # Using command with access_level "3" # noqa: E501 - "start_date": fields.Datetime.now(), - } - ) - ) - recs = self.CommandLog.with_user(self.manager).search( - [("id", "=", high_access_log.id)] - ) - self.assertNotIn( - high_access_log, - recs, - "Manager should not be able to read logs with access_level > '2'", - ) - - # Case 4: Manager should not be able to read when he is not - # in users_ids or manager_ids - self.server_test_1.write( - { - "user_ids": [(5, 0, 0)], - "manager_ids": [(5, 0, 0)], - } - ) - recs = self.CommandLog.with_user(self.manager).search( - [("id", "in", [self.command_log_1.id, self.command_log_2.id])] - ) - self.assertNotIn( - self.command_log_1, - recs, - "Manager should not be able to read logs when he is not" - " in users_ids or manager_ids", - ) - - def test_root_read_only_access(self): - """Root can read all command logs, but cannot create/modify/delete""" - # Create test logs with sudo() - test_logs = self.CommandLog.sudo().create( - [ - { - "server_id": self.server_2.id, - "command_id": command.id, - "start_date": fields.Datetime.now(), - } - for command in [ - self.command_level_1, - self.command_level_2, - self.command_level_3, - ] - ] - ) - # Root cannot create logs - with self.assertRaises(AccessError): - self.CommandLog.with_user(self.root).create( - { - "server_id": self.server_2.id, - "command_id": self.command_level_1.id, - "start_date": fields.Datetime.now(), - } - ) - - # Root cannot modify logs - with self.assertRaises(AccessError): - test_logs.with_user(self.root).write({"start_date": fields.Datetime.now()}) - - # Root cannot delete logs - with self.assertRaises(AccessError): - test_logs.with_user(self.root).unlink() - - # Root should be able to read all logs regardless of: - # - access_level - # - server relationships - # - who created them - recs = self.CommandLog.with_user(self.root).search( - [("id", "in", test_logs.ids)] - ) - self.assertEqual( - len(recs), - 3, - "Root should have unrestricted read access to all logs", - ) - - # Test read on all records - all_recs = self.CommandLog.with_user(self.root).search([]) - self.assertGreater( - len(all_recs), - 0, - "Root should be able to read all command logs", - ) diff --git a/addons/cetmix_tower_server/tests/test_command_wizard.py b/addons/cetmix_tower_server/tests/test_command_wizard.py deleted file mode 100644 index ca44338..0000000 --- a/addons/cetmix_tower_server/tests/test_command_wizard.py +++ /dev/null @@ -1,572 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.exceptions import AccessError, ValidationError - -from .common import TestTowerCommon - - -class TestTowerCommandWizard(TestTowerCommon): - """Test Tower Command Run Wizard""" - - def test_user_access_rules(self): - """Test user access rules""" - - # Add Bob to `root` group in order to create a wizard - self.add_to_group(self.user_bob, "cetmix_tower_server.group_root") - - # Create new wizard - test_wizard = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .create( - { - "server_ids": [self.server_test_1.id], - "command_id": self.command_create_dir.id, - } - ) - ).with_user(self.user_bob) - - # Force rendered code computation - test_wizard._compute_rendered_code() - - # Remove bob from all cxtower_server groups - self.remove_from_group( - self.user_bob, - [ - "cetmix_tower_server.group_user", - "cetmix_tower_server.group_manager", - "cetmix_tower_server.group_root", - ], - ) - # Ensure that regular user cannot execute command in wizard - with self.assertRaises(AccessError): - test_wizard.run_command_in_wizard() - - # Add bob back to `user` group and try again - self.add_to_group(self.user_bob, "cetmix_tower_server.group_user") - with self.assertRaises(AccessError): - test_wizard.run_command_in_wizard() - - # Now promote bob to `manager` group and try again - self.add_to_group(self.user_bob, "cetmix_tower_server.group_manager") - test_wizard.run_command_in_wizard() - - def test_execute_code_without_a_command(self): - """Run command code without a command selected""" - - # Add Bob to `root` group in order to create a wizard - self.add_to_group(self.user_bob, "cetmix_tower_server.group_root") - - # Create new wizard - test_wizard = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .create( - { - "server_ids": [self.server_test_1.id], - } - ) - ).with_user(self.user_bob) - - # Should not allow to run command on server if no command is selected - with self.assertRaises(ValidationError): - test_wizard.run_command_on_server() - - def test_run_command_on_server_access_rights(self): - """Test access rights for executing command on server""" - - # Add Bob to `root` group - self.add_to_group(self.user_bob, "cetmix_tower_server.group_root") - - # Create new wizard with Bob as a root user - test_wizard = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .create( - { - "server_ids": [self.server_test_1.id], - "command_id": self.command_create_dir.id, - } - ) - ).with_user(self.user_bob) - - # Ensure command can be executed by root - test_wizard.run_command_on_server() - - # Remove Bob from all tower server groups - self.remove_from_group( - self.user_bob, - [ - "cetmix_tower_server.group_user", - "cetmix_tower_server.group_manager", - "cetmix_tower_server.group_root", - ], - ) - - # Ensure that regular user cannot execute command on server - with self.assertRaises(AccessError): - test_wizard.run_command_on_server() - - # Add Bob to `user` group and ensure he can execute commands - self.add_to_group(self.user_bob, "cetmix_tower_server.group_user") - test_wizard.run_command_on_server() - # Ensure that Bob has access to path field but can't read its value - allowed_path = ( - self.user_bob.has_group("cetmix_tower_server.group_manager") - and test_wizard.path - ) - - self.assertEqual(allowed_path, False) - # Ensure that Bob can write to the path field as a member of `group_user` - # the result will be None - test_wizard.write({"path": "/new/invalid/path"}) - allowed_path = ( - test_wizard.path - if self.user_bob.has_group("cetmix_tower_server.group_manager") - and test_wizard.path - else None - ) - self.assertEqual(allowed_path, None) - - # Add Bob to `manager` group and ensure access to execute commands - self.add_to_group(self.user_bob, "cetmix_tower_server.group_manager") - test_wizard.run_command_on_server() - # Check that path access is valid for the manager - test_wizard.read(["path"]) - - def test_run_command_with_sensitive_vars_on_server_access_rights(self): - """Test access rights for executing command on server""" - # create new command - command = self.Command.create( - { - "name": "Create new command", - "action": "python_code", - "code": """ - properties = { - "Server Name": {{ tower.server.name }}, - "Server Reference": {{ tower.server.reference }}, - "SSH Username": {{ tower.server.username }}, - "IPv4 Address": {{ tower.server.ipv4 }}, - "IPv6 Address": {{ tower.server.ipv6 }}, - "Partner Name": {{ tower.server.partner_name }} - } - result = {"exit_code": 0, "message": properties} - """, - "access_level": "1", - } - ) - - # Add Bob to `root` group in order to create a wizard - self.add_to_group(self.user_bob, "cetmix_tower_server.group_root") - - server = self.Server.with_user(self.user_bob).create( - { - "name": "Test 2", - "ip_v4_address": "localhost", - "ssh_username": "root", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - } - ) - - self.remove_from_group( - self.user_bob, - [ - "cetmix_tower_server.group_user", - "cetmix_tower_server.group_manager", - "cetmix_tower_server.group_root", - ], - ) - - # Add user bob to group user - self.add_to_group(self.user_bob, "cetmix_tower_server.group_user") - - # Create new wizard with Bob - test_wizard = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .create( - { - "server_ids": [server.id], - "command_id": command.id, - } - ) - ).with_user(self.user_bob) - - # Add Bob as a user to the command - command.write({"user_ids": [(4, self.user_bob.id)]}) - - # Ensure command can be executed by user - test_wizard.run_command_on_server() - - def test_run_command_in_wizard_multiple_servers(self): - """ - Test that raises an error when multiple servers are selected - """ - - # Add Bob to `root` group in order to create a wizard - - server_test_2 = self.Server.create( - { - "name": "Test 2", - "ip_v4_address": "localhost", - "ssh_username": "root", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - } - ) - - self.add_to_group(self.user_bob, "cetmix_tower_server.group_root") - - # Create new wizard with multiple servers selected - test_wizard = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .create( - { - "server_ids": [self.server_test_1.id, server_test_2.id], - "command_id": self.command_create_dir.id, - } - ) - ).with_user(self.user_bob) - - # Force rendered code computation - test_wizard._compute_rendered_code() - - # Ensure that executing command with multiple servers - # selected raises a ValidationError - with self.assertRaises( - ValidationError, - msg="You cannot run custom code on multiple servers at once.", - ): - test_wizard.run_command_in_wizard() - - # Now, test with a single server selected - test_wizard.server_ids = [self.server_test_1.id] - - # Ensure that executing command works with a single server selected - test_wizard.run_command_in_wizard() - self.assertTrue( - test_wizard.result, - msg="Command execution should succeed with a single server selected", - ) - - def test_custom_variable_values_creation(self): - """ - Test that custom variable values are created properly - when command has variables - """ - # Add manager as server user - self.server_test_1.write({"user_ids": [(4, self.manager.id)]}) - - # Create variables that will be used in command - variable = self.Variable.create( - { - "name": "Test Variable", - "reference": "test_var", - "variable_type": "s", # string type - } - ) - option_variable = self.Variable.create( - { - "name": "Option Variable", - "reference": "opt_var", - "variable_type": "o", # option type - } - ) - option = self.VariableOption.create( - { - "name": "Test Option", - "value_char": "option_value", - "variable_id": option_variable.id, - } - ) - - # Add variable values to server - self.VariableValue.create( - [ - { - "variable_id": variable.id, - "server_id": self.server_test_1.id, - "value_char": "server value", - }, - { - "variable_id": option_variable.id, - "server_id": self.server_test_1.id, - "value_char": "option_value", - }, - ] - ) - - # Create command that uses these variables in its code - command = self.Command.create( - { - "name": "Test Command with Variables", - "action": "ssh_command", - "code": "echo {{ test_var }} && echo {{ opt_var }}", - } - ) - - # Create wizard - wizard = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.manager) - .create( - { - "server_ids": [self.server_test_1.id], - "command_id": command.id, - "action": "ssh_command", - } - ) - ) - - # Trigger onchange to generate custom_variable_values - wizard._onchange_command_variable_ids() - - # Check that custom variable values were created - self.assertEqual(len(wizard.custom_variable_value_ids), 2) - - # Check char variable value - char_value = wizard.custom_variable_value_ids.filtered( - lambda v: v.variable_id == variable - ) - self.assertTrue(char_value) - self.assertEqual(char_value.value_char, "server value") - - # Check option variable value - option_value = wizard.custom_variable_value_ids.filtered( - lambda v: v.variable_id == option_variable - ) - self.assertTrue(option_value) - self.assertEqual(option_value.value_char, "option_value") - self.assertEqual(option_value.option_id, option) - - # Try to change variable value when user doesn't have write access - char_value.value_char = "custom value" - - # Run command - wizard.run_command_on_server() - - # Get latest command log - command_log = self.env["cx.tower.command.log"].search( - [ - ("server_id", "=", self.server_test_1.id), - ("command_id", "=", command.id), - ], - order="create_date desc", - limit=1, - ) - - # Verify that original server values were used - self.assertEqual(command_log.code, "echo server value && echo option_value") - - def test_custom_variable_values_with_manager_access(self): - """ - Test that custom variable values are applied - when manager has write access - """ - # Add manager as server manager - self.server_test_1.write({"manager_ids": [(4, self.manager.id)]}) - - # Create variables that will be used in command - variable = self.Variable.create( - { - "name": "Test Variable", - "reference": "test_var", - "variable_type": "s", # string type - } - ) - - # Add variable value to server - self.VariableValue.create( - { - "variable_id": variable.id, - "server_id": self.server_test_1.id, - "value_char": "server value", - } - ) - - # Create command that uses the variable - command = self.Command.create( - { - "name": "Test Command with Variables", - "action": "ssh_command", - "code": "echo {{ test_var }}", - } - ) - - # Create wizard - wizard = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.manager) - .create( - { - "server_ids": [self.server_test_1.id], - "command_id": command.id, - "action": "ssh_command", - } - ) - ) - - # Trigger onchange to generate custom_variable_value_ids - wizard._onchange_command_variable_ids() - - # Modify variable value - wizard.custom_variable_value_ids.filtered( - lambda v: v.variable_id == variable - ).value_char = "manager value" - - # Run command - wizard.run_command_on_server() - - # Get latest command log - command_log = self.env["cx.tower.command.log"].search( - [ - ("server_id", "=", self.server_test_1.id), - ("command_id", "=", command.id), - ], - order="create_date desc", - limit=1, - ) - - # Verify that custom value was used - self.assertEqual(command_log.code, "echo manager value") - - def test_default_applicability_for_regular_and_manager(self): - """sets applicability='this' for regular users, keeps default for managers.""" - # Regular user (no special groups) - default_usr = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .default_get(["applicability"]) - ) - self.assertEqual(default_usr.get("applicability"), "this") - - # Manager user should receive the original default ("shared") - self.add_to_group(self.user_bob, "cetmix_tower_server.group_manager") - default_mgr = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .default_get(["applicability"]) - ) - self.assertEqual(default_mgr.get("applicability"), "shared") - - def test_compute_show_servers_behavior(self): - """Should enforce 'this' for regular users but preserve manager choice.""" - # Grant Bob the basic 'user' group so he can read servers and create the wizard - self.add_to_group(self.user_bob, "cetmix_tower_server.group_user") - - # Ensure Bob has read access to the first server - self.server_test_1.write({"user_ids": [(4, self.user_bob.id)]}) - # Create a second server and grant Bob read access to it - srv2 = self.Server.create( - { - "name": "Server 2", - "ip_v4_address": "127.0.0.2", - "ssh_username": "root", - "ssh_password": "pwd", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - } - ) - srv2.write({"user_ids": [(4, self.user_bob.id)]}) - - # --- Regular user scenario --- - wiz_usr = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .create({"server_ids": [self.server_test_1.id, srv2.id]}) - ) - # Compute show_servers under Bob; he should see both servers - wiz_usr._compute_show_servers() - self.assertTrue(wiz_usr.show_servers) - # Enforcement should set applicability to 'this' - self.assertEqual(wiz_usr.applicability, "this") - - # --- Manager user scenario --- - self.add_to_group(self.user_bob, "cetmix_tower_server.group_manager") - # Grant Bob manager access to both servers - self.server_test_1.write({"manager_ids": [(4, self.user_bob.id)]}) - srv2.write({"manager_ids": [(4, self.user_bob.id)]}) - - wiz_mgr = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.user_bob) - .create({"server_ids": [self.server_test_1.id, srv2.id]}) - ) - # Compute show_servers under Bob as manager - wiz_mgr._compute_show_servers() - # Manager should also see both servers - self.assertTrue(wiz_mgr.show_servers) - # Enforcement should not override manager's choice of 'shared' - self.assertEqual(wiz_mgr.applicability, "shared") - - def test_required_variable_validation(self): - """ - Wizard must block execution when a required variable is empty - and allow it after the value is provided. - """ - # Create a required variable - var = self.Variable.create( - { - "name": "Req Var", - "reference": "req_var", - "variable_type": "s", - } - ) - self.VariableValue.create( - { - "variable_id": var.id, - "server_id": self.server_test_1.id, - "required": True, - "value_char": "", - } - ) - - # Create command that uses this variable - cmd = self.Command.create( - { - "name": "Echo Req Var", - "action": "ssh_command", - "code": "echo {{ req_var }}", - "variable_ids": [(4, var.id)], - } - ) - - self.server_test_1.write({"user_ids": [(4, self.manager.id)]}) - - # Create wizard as manager user - wiz = ( - self.env["cx.tower.command.run.wizard"] - .with_user(self.manager) - .create( - { - "server_ids": [self.server_test_1.id], - "command_id": cmd.id, - } - ) - ) - - # Create lines of configuration - wiz._onchange_command_variable_ids() - wiz._compute_has_missing_required_values() - - # Test blocking behavior - self.assertTrue(wiz.has_missing_required_values) - with self.assertRaises(ValidationError): - wiz.run_command_on_server() - - # Fill the value directly in the wizard line - wiz.custom_variable_value_ids.filtered( - lambda line: line.variable_id == var - ).value_char = "filled" - - # Recompute the flag - wiz._compute_has_missing_required_values() - self.assertFalse(wiz.has_missing_required_values) - - # Now the execution should pass - wiz.run_command_on_server() diff --git a/addons/cetmix_tower_server/tests/test_file.py b/addons/cetmix_tower_server/tests/test_file.py deleted file mode 100644 index f2feb6f..0000000 --- a/addons/cetmix_tower_server/tests/test_file.py +++ /dev/null @@ -1,482 +0,0 @@ -from odoo import exceptions -from odoo.exceptions import AccessError - -from .common import TestTowerCommon - - -class TestTowerFile(TestTowerCommon): - @classmethod - def setUpClass(cls): - super().setUpClass() - - cls.file_template = cls.FileTemplate.create( - { - "name": "Test", - "file_name": "test.txt", - "server_dir": "/var/tmp", - "code": "Hello, world!", - } - ) - cls.file = cls.File.create( - { - "name": "tower_demo_1.txt", - "source": "tower", - "template_id": cls.file_template.id, - "server_id": cls.server_test_1.id, - } - ) - cls.file_2 = cls.File.create( - { - "name": "test.txt", - "source": "server", - "server_id": cls.server_test_1.id, - "server_dir": "/var/tmp", - } - ) - - # Create a dummy Server record that will be referenced by file records. - cls.server = cls.Server.create( - { - "name": "Test Server", - "manager_ids": [(6, 0, [cls.manager.id])], - "user_ids": [(6, 0, [cls.user.id])], - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "skip_host_key": True, - "os_id": cls.os_debian_10.id, - "ip_v4_address": "localhost", - } - ) - - def test_user_read_access(self): - """ - Test that a user in the custom User group can read a file record - when their ID is in the related server's user_ids. - """ - file_record = self.File.create( - { - "name": "Test File", - "server_dir": "/tmp", - "file_type": "text", - "source": "tower", - "server_id": self.server.id, - } - ) - # As the user, the file record should be visible. - files_for_user = self.File.with_user(self.user).search( - [("id", "=", file_record.id)] - ) - self.assertTrue( - files_for_user, - "User should be able to read the file record " - "because they are in server.user_ids.", - ) - - # Remove user from server.user_ids. - self.server.write({"user_ids": [(3, self.user.id)]}) - files_for_user = self.File.with_user(self.user).search( - [("id", "=", file_record.id)] - ) - self.assertFalse( - files_for_user, - "User should not be able to read the file record " - "because he is not in server.user_ids.", - ) - - def test_manager_write_create_access(self): - """ - Test that a manager in the custom Manager group can create and write - file records when his ID is in the related server's manager_ids. - """ - # Test creation: the manager is in server.manager_ids. - file_record = self.File.with_user(self.manager).create( - { - "name": "Manager Created File", - "server_dir": "/tmp", - "file_type": "text", - "source": "tower", - "server_id": self.server.id, - } - ) - self.assertTrue( - file_record, - "Manager should be able to create a file record " - "because they are in server.manager_ids.", - ) - - # Test updating (write access). - try: - file_record.with_user(self.manager).write({"name": "Manager Updated File"}) - except AccessError: - self.fail( - "Manager should be able to update the file record " - "because he is in server.manager_ids." - ) - self.assertEqual( - file_record.with_user(self.manager).name, - "Manager Updated File", - "File record name should be updated by the manager.", - ) - - # Test that a manager who is not in the server's manager_ids - # cannot write or create. - # Remove manager from server.manager_ids. - self.server.write({"manager_ids": [(3, self.manager.id)]}) - # Create a file record on this server. - file_record2 = self.File.create( - { - "name": "File on Server Without Manager", - "server_dir": "/tmp", - "file_type": "text", - "source": "tower", - "server_id": self.server.id, - } - ) - with self.assertRaises(AccessError): - file_record2.with_user(self.manager).write({"name": "Should Not Update"}) - - # Test create access for a manager not in manager_ids. - with self.assertRaises(AccessError): - self.File.with_user(self.manager).create( - { - "name": "Invalid File", - "server_dir": "/tmp", - "file_type": "text", - "source": "tower", - "server_id": self.server.id, - } - ) - - def test_manager_unlink_access(self): - """ - Test that a manager in the custom Manager group can unlink (delete) a file - record only if he is in the related server's manager_ids - and they are the record's creator. - """ - # Scenario 1: Record created by the manager. - file_record = self.File.with_user(self.manager).create( - { - "name": "File to Delete", - "server_dir": "/tmp", - "file_type": "text", - "source": "tower", - "server_id": self.server.id, - } - ) - try: - file_record.with_user(self.manager).unlink() - except AccessError: - self.fail( - "Manager should be able to delete their own file" - " record when in server.manager_ids." - ) - - # Scenario 2: Record created by someone else (e.g., the admin). - file_record2 = self.File.create( - { - "name": "File Not Deletable by Manager", - "server_dir": "/tmp", - "file_type": "text", - "source": "tower", - "server_id": self.server.id, - } - ) - with self.assertRaises(AccessError): - file_record2.with_user(self.manager).unlink() - - def test_upload_file(self): - """ - Upload file from tower to server - """ - self.file.action_push_to_server() - self.assertEqual(self.file.server_response, "ok") - - def test_delete_file(self): - """ - Delete file remotely from server - """ - result = self.file.action_delete_from_server() - self.assertTrue(isinstance(result, dict)) - self.assertEqual(result["params"]["message"], "File deleted!") - - def test_delete_file_access(self): - """ - Test delete file access - """ - with self.assertRaises(exceptions.AccessError): - self.file.with_user(self.user_bob).delete(raise_error=True) - - def test_download_file(self): - """ - Download file from server to tower - """ - self.file_2.action_pull_from_server() - self.assertEqual(self.file_2.code, "ok") - - self.file_2.name = "binary.zip" - res = self.file_2.action_pull_from_server() - self.assertTrue( - isinstance(res, dict) and res["tag"] == "display_notification", - msg=( - "If file type is 'text', then the result must be a dict " - "representing the display_notification action." - ), - ) - - def test_get_current_server_code(self): - """ - Download file from server to tower - """ - self.file.action_push_to_server() - self.assertEqual(self.file.server_response, "ok") - - self.file.action_get_current_server_code() - self.assertEqual(self.file.code_on_server, "ok") - - def test_modify_template_code(self): - """Test how template code modification affects related files""" - code = "Pepe frog is happy as always" - self.file_template.code = code - - # Check file code before modifications - self.assertTrue( - self.file.code == code, - msg="File code must be the same " - "as template code before any modifications", - ) - # Check file rendered code before modifications - self.assertTrue( - self.file.rendered_code == code, - msg="File rendered code must be the same" - " as template code before any modifications", - ) - - # Make possible to modify file code - self.file.action_unlink_from_template() - - # Check if template was removed from file - self.assertFalse( - self.file.template_id, - msg="File template should be removed after modifying code.", - ) - - # Check if file code remains the same - self.assertTrue( - self.file.code == code, msg="File code should be the same as template." - ) - - def test_modify_template_related_files(self): - """ - Check that after change file template - all related files will update - """ - self.assertEqual(self.file_template.file_name, "test.txt") - # related files - self.assertTrue( - all(file.name == "test.txt" for file in self.file_template.file_ids) - ) - - # update file template name - self.file_template.file_name = "new_test.txt" - # Related files must updated - self.assertTrue( - all(file.name == "new_test.txt" for file in self.file_template.file_ids) - ) - - self.assertEqual(self.file_template.code, "Hello, world!") - # update file template code - self.file_template.code = "New code" - # Related files must updated - self.assertTrue( - all(file.code == "New code" for file in self.file_template.file_ids) - ) - - def test_create_file_with_template(self): - """ - Test if file is created with template code - """ - file_template = self.env["cx.tower.file.template"].create( - { - "name": "Test", - "file_name": "test.txt", - "server_dir": "/var/tmp", - "code": "Hello, world!", - } - ) - - file = file_template.create_file( - server=self.server_test_1, - server_dir=file_template.server_dir, - if_file_exists="overwrite", - ) - self.assertEqual(file.code, self.file_template.code) - self.assertEqual(file.template_id, file_template) - self.assertEqual(file.server_id, self.server_test_1) - self.assertEqual(file.source, "tower") - self.assertEqual(file.server_dir, self.file_template.server_dir) - - with self.assertRaises(exceptions.ValidationError): - file_template.create_file( - server=self.server_test_1, - server_dir=file_template.server_dir, - if_file_exists="raise", - ) - - another_file = file_template.create_file( - server=self.server_test_1, - server_dir=file_template.server_dir, - if_file_exists="skip", - ) - self.assertEqual(another_file, file) - - def test_create_file_with_template_custom_server_dir(self): - """ - Test if file is created with template code and custom server dir - """ - file_template = self.env["cx.tower.file.template"].create( - { - "name": "Test", - "file_name": "test.txt", - "server_dir": "/var/tmp", - "code": "Hello, world!", - } - ) - - file = file_template.create_file( - server=self.server_test_1, server_dir="/var/tmp/custom" - ) - self.assertEqual(file.code, self.file_template.code) - self.assertEqual(file.template_id, file_template) - self.assertEqual(file.server_id, self.server_test_1) - self.assertEqual(file.source, "tower") - self.assertEqual(file.server_dir, "/var/tmp/custom") - - with self.assertRaises(exceptions.ValidationError): - file_template.create_file( - server=self.server_test_1, - server_dir="/var/tmp/custom", - if_file_exists="raise", - ) - - another_file = file_template.create_file( - server=self.server_test_1, - server_dir="/var/tmp/custom", - if_file_exists="skip", - ) - self.assertEqual(another_file, file) - - def test_file_with_secret_key(self): - """ - Test case to verify that when a file includes a secret reference, - the secret key is automatically linked with the file. - """ - - # Create a secret key - secret_python_key = self.Key.create( - { - "name": "python", - "reference": "PYTHON", - "secret_value": "secretPythonCode", - "key_type": "s", - } - ) - - # Create a file template with a reference to the secret key - file_template = self.env["cx.tower.file.template"].create( - { - "name": "Test", - "file_name": "test.txt", - "server_dir": "/var/tmp", - "code": "Please use this secret #!cxtower.secret.PYTHON!#", - } - ) - - # Create a file from the file template - file = file_template.create_file( - server=self.server_test_1, server_dir="/var/tmp/custom" - ) - - # Assert that the file's code matches the file template's code - self.assertEqual( - file.code, - file_template.code, - msg="The file's code does not match the file template's code.", - ) - - # Assert that the secret key is associated with the file - self.assertIn( - secret_python_key, - file.secret_ids, - msg="The secret key is not associated with the file.", - ) - - # Update the file's code to remove the secret reference - file.code = "Only text" - - self.assertFalse( - file.secret_ids, - msg=( - "The secret_ids field should be empty after " - "removing the secret reference from file." - ), - ) - - def test_file_with_sensitive_variable(self): - """ - Test case to verify that user has access to use file with sensitive variables. - """ - # Create file with sensitive variable - file = self.File.create( - { - "source": "tower", - "name": "test.txt", - "server_id": self.server_test_1.id, - "code": "'IPv4 Address': {{ tower.server.ipv4 }}", - } - ) - # Remove user_bob from all cx_tower_server groups - self.remove_from_group( - self.user_bob, - [ - "cetmix_tower_server.group_user", - "cetmix_tower_server.group_manager", - "cetmix_tower_server.group_root", - ], - ) - # Add bob to user group - self.add_to_group(self.user_bob, "cetmix_tower_server.group_user") - # Add bob as subscriber of the server to allow upload file - self.server_test_1.write({"user_ids": [(4, self.user_bob.id)]}) - # Upload file to server - self.assertTrue(file.server_response != "ok") - file.with_user(self.user_bob).action_push_to_server() - self.assertEqual(file.server_response, "ok") - - def test_sanitize_values(self): - """ - Test case to verify that the sanitize_values method works correctly. - """ - # 1. Root directory - values = self.File._sanitize_values({"server_dir": "/"}) - self.assertEqual(values["server_dir"], "/") - - # 2. Trailing slash - values = self.File._sanitize_values({"server_dir": "/var/tmp/"}) - self.assertEqual(values["server_dir"], "/var/tmp") - - # 3. Trailing whitespace - values = self.File._sanitize_values({"server_dir": "/var/tmp/ "}) - self.assertEqual(values["server_dir"], "/var/tmp") - - # 4. Leading whitespace - values = self.File._sanitize_values({"server_dir": " /var/tmp/"}) - self.assertEqual(values["server_dir"], "/var/tmp") - - # 5. Leading and trailing whitespace - values = self.File._sanitize_values({"server_dir": " /var/tmp/ "}) - self.assertEqual(values["server_dir"], "/var/tmp") - - # 6. Leading and trailing whitespace just one slash - values = self.File._sanitize_values({"server_dir": " / "}) - self.assertEqual(values["server_dir"], "/") diff --git a/addons/cetmix_tower_server/tests/test_file_template.py b/addons/cetmix_tower_server/tests/test_file_template.py deleted file mode 100644 index 7b4275a..0000000 --- a/addons/cetmix_tower_server/tests/test_file_template.py +++ /dev/null @@ -1,234 +0,0 @@ -from odoo.exceptions import AccessError - -from .common import TestTowerCommon - - -class TestCxTowerFileTemplateAccessRules(TestTowerCommon): - def test_user_no_access(self): - """ - Verify that a user in the User group has no access - to any file template records. - """ - # Create a file template record as admin. - record = self.FileTemplate.create( - { - "name": "Template 1", - "file_name": "template1.txt", - "code": "Sample code", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - } - ) - # As the user, search for the record – expect no records. - with self.assertRaises(AccessError): - self.FileTemplate.with_user(self.user).search([("id", "=", record.id)]) - - # Attempting to create a record as a user should raise an AccessError. - with self.assertRaises(AccessError): - self.FileTemplate.with_user(self.user).create( - { - "name": "Template 2", - "file_name": "user_template.txt", - "code": "User code", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - } - ) - - def test_manager_read_access(self): - """ - Verify that a manager can read file template records - if he is not in user_ids or manager_ids. - """ - # Create a record with the manager in manager_ids. - rec1 = self.FileTemplate.create( - { - "name": "Template 1", - "file_name": "template_manager.txt", - "code": "Manager code", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - # Create a record with the manager in user_ids. - rec2 = self.FileTemplate.create( - { - "name": "Template 2", - "file_name": "template_user.txt", - "code": "User code", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - "user_ids": [(6, 0, [self.manager.id])], - } - ) - # Create a record that does not include the manager. - rec3 = self.FileTemplate.create( - { - "name": "Template 3", - "file_name": "template_none.txt", - "code": "None code", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - } - ) - recs = self.FileTemplate.with_user(self.manager).search([]) - self.assertIn(rec1, recs, "Manager should read records if in manager_ids.") - self.assertIn(rec2, recs, "Manager should read records if in user_ids.") - self.assertNotIn( - rec3, - recs, - "Manager should not see records if not in user_ids or manager_ids.", - ) - - def test_manager_write_create_access(self): - """ - Verify that a manager can write and create file template records - only if he is in manager_ids. - """ - # Create a record with manager_ids including the manager. - rec = self.FileTemplate.create( - { - "name": "Template 1", - "file_name": "template_for_update.txt", - "code": "Initial code", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - # Manager should be able to update the record. - try: - rec.with_user(self.manager).write({"file_name": "template_updated.txt"}) - except AccessError: - self.fail( - "Manager should be able to update the record when in manager_ids." - ) - self.assertEqual(rec.with_user(self.manager).file_name, "template_updated.txt") - - # Manager should be able to create a record if included in manager_ids. - rec2 = self.FileTemplate.with_user(self.manager).create( - { - "name": "Template 2", - "file_name": "manager_created_template.txt", - "code": "Manager created", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - self.assertTrue( - rec2, - "Manager should be able to create a record when included in manager_ids.", - ) - - # Creating a record without including the manager should raise an AccessError. - with self.assertRaises(AccessError): - self.FileTemplate.with_user(self.manager).create( - { - "name": "Template 3", - "file_name": "invalid_template.txt", - "code": "Invalid", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - "manager_ids": [(5, 0, 0)], - } - ) - - def test_manager_unlink_access(self): - """ - Verify that a manager can delete a file template record only if - he is in manager_ids and is the creator. - """ - # Scenario 1: Record created by the manager. - rec = self.FileTemplate.with_user(self.manager).create( - { - "name": "Template 1", - "file_name": "template_to_delete.txt", - "code": "Code to delete", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - try: - rec.with_user(self.manager).unlink() - except AccessError: - self.fail( - "Manager should be able to delete a record " - "he created when in manager_ids." - ) - # Scenario 2: Record created by admin (or another user) - # even though manager_ids includes the manager. - rec2 = self.FileTemplate.create( - { - "name": "Template 2", - "file_name": "template_not_deletable.txt", - "code": "Admin created code", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - with self.assertRaises(AccessError): - rec2.with_user(self.manager).unlink() - - def test_root_unrestricted_access(self): - """ - Verify that a user in the Root group has unlimited access - to all file template records. - """ - # Create a file template record (with no particular restrictions). - rec = self.FileTemplate.create( - { - "name": "Template 1", - "file_name": "template_for_root.txt", - "code": "Root code", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - } - ) - # As the root user, the record should be visible. - recs = self.FileTemplate.with_user(self.root).search([("id", "=", rec.id)]) - self.assertTrue(recs, "Root should see the record regardless of restrictions.") - # Root should be able to update the record. - try: - rec.with_user(self.root).write({"file_name": "root_updated_template.txt"}) - except AccessError: - self.fail("Root should be able to update the record without restrictions.") - self.assertEqual( - rec.with_user(self.root).file_name, "root_updated_template.txt" - ) - # Root should be able to create a record. - rec2 = self.FileTemplate.with_user(self.root).create( - { - "name": "Template 2", - "file_name": "root_created_template.txt", - "code": "Created by root", - "server_dir": "/templates", - "file_type": "text", - "source": "tower", - } - ) - self.assertTrue( - rec2, "Root should be able to create a record without restrictions." - ) - # Root should be able to delete a record. - rec2.with_user(self.root).unlink() - recs_after = self.FileTemplate.with_user(self.root).search( - [("id", "=", rec2.id)] - ) - self.assertFalse( - recs_after, "Root should be able to delete the record without restrictions." - ) diff --git a/addons/cetmix_tower_server/tests/test_key.py b/addons/cetmix_tower_server/tests/test_key.py deleted file mode 100644 index c633417..0000000 --- a/addons/cetmix_tower_server/tests/test_key.py +++ /dev/null @@ -1,919 +0,0 @@ -from odoo.exceptions import AccessError, ValidationError - -from .common import TestTowerCommon - - -class TestTowerKey(TestTowerCommon): - """Test class for tower key.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create another manager for testing - cls.manager_2 = cls.Users.create( - { - "name": "Second Manager", - "login": "manager2", - "email": "manager2@test.com", - "groups_id": [(4, cls.env.ref("cetmix_tower_server.group_manager").id)], - } - ) - - # Create test servers - cls.server_1 = cls.Server.create( - { - "name": "Test Server 1", - "ip_v4_address": "192.168.1.1", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - } - ) - cls.server_2 = cls.Server.create( - { - "name": "Test Server 2", - "ip_v4_address": "192.168.1.2", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - } - ) - cls.test_key = cls.Key.create( - {"name": "Test Key", "key_type": "s", "secret_value": "test value"} - ) - - def test_key_creation(self): - """ - Test key creation. - We override create method so need to check if reference is generated properly - """ - - # -- 1-- - # Check new key values - key_one = self.Key.create( - {"name": " test key meme ", "secret_value": "test value", "key_type": "s"} - ) - self.assertEqual( - key_one.reference, "test_key_meme", "Reference must be 'test_key_meme'" - ) - self.assertEqual( - key_one.name, - "test key meme", - "Trailing and leading whitespaces must be removed from name", - ) - - def test_extract_key_strings(self): - """Check if key strings are extracted properly""" - code = ( - "Hey #!cxtower.secret.MEME_KEY!# & Doge #!cxtower.secret.DOGE_KEY !# so " - "like #!cxtower.secret.MEME_KEY!#!\n" - "They make #!memes together." - "And this is another string for the same #!cxtower.secret.MEME_KEY !#" - ) - key_strings = self.Key._extract_key_strings(code) - self.assertEqual(len(key_strings), 3, "Must be 3 key stings") - self.assertIn( - "#!cxtower.secret.MEME_KEY!#", - key_strings, - "Key string must be in key strings", - ) - self.assertIn( - "#!cxtower.secret.DOGE_KEY !#", - key_strings, - "Key string must be in key strings", - ) - self.assertIn( - "#!cxtower.secret.MEME_KEY !#", - key_strings, - "Key string must be in key strings", - ) - - def test_parse_key_string(self): - """Check if key string is parsed correctly""" - - # Test global key - doge_key = self.Key.create( - { - "name": "doge key", - "reference": "DOGE_KEY", - "secret_value": "Doge dog", - "key_type": "s", - } - ) - key_string = "#!cxtower.secret.DOGE_KEY!#" - key_value = self.Key._parse_key_string(key_string) - self.assertEqual(key_value, "Doge dog", "Key value doesn't match") - - # Test the same key string but with some spaces before the key terminator - key_string = "#!cxtower.secret.DOGE_KEY !#" - key_value = self.Key._parse_key_string(key_string) - self.assertEqual(key_value, "Doge dog", "Key value doesn't match") - - # Test partner specific key - self.KeyValue.create( - { - "key_id": doge_key.id, - "secret_value": "Doge partner", - "partner_id": self.user_bob.partner_id.id, - } - ) - # compose kwargs - kwargs = { - "partner_id": self.user_bob.partner_id.id, - "server_id": self.server_test_1.id, - } - key_value = self.Key._parse_key_string(key_string, **kwargs) - self.assertEqual(key_value, "Doge partner", "Key value doesn't match") - - # Test server specific key - self.KeyValue.create( - { - "key_id": doge_key.id, - "secret_value": "Doge server", - "server_id": self.server_test_1.id, - } - ) - key_value = self.Key._parse_key_string(key_string, **kwargs) - - # Test server and partner specific key - self.KeyValue.create( - { - "key_id": doge_key.id, - "secret_value": "Doge server and partner", - "server_id": self.server_test_1.id, - "partner_id": self.user_bob.partner_id.id, - } - ) - key_value = self.Key._parse_key_string(key_string, **kwargs) - self.assertEqual( - key_value, "Doge server and partner", "Key value doesn't match" - ) - - # Test missing key - key_string = "#!cxtower.secret.ANOTHER_KEY!#" - key_value = self.Key._parse_key_string(key_string) - self.assertIsNone(key_value, "Key value must be 'None'") - - # Test missformatted key - key_string = "#!cxtower.ANOTHER_KEY!#" - key_value = self.Key._parse_key_string(key_string) - self.assertIsNone(key_value, "Key value must be 'None'") - - # Test another missformatted key - key_string = "#!cxtower.notasecret.DOGE_KEY!#" - key_value = self.Key._parse_key_string(key_string) - self.assertIsNone(key_value, "Key value must be 'None'") - - def test_resolve_key(self): - """Check generic key resolver""" - self.Key.create( - { - "name": "doge key", - "reference": "DOGE_KEY", - "secret_value": "Doge dog", - "key_type": "s", - } - ) - - # Existing key - key_value = self.Key._resolve_key("secret", "DOGE_KEY") - self.assertEqual(key_value, "Doge dog", "Key value doesn't match") - - # Non existing key - key_value = self.Key._resolve_key("server", "PEPE_KEY") - self.assertIsNone(key_value, "Key value must be 'None'") - - def test_resolve_key_type_secret(self): - """Check 'secret' type key resolver""" - doge_key = self.Key.create( - { - "name": "doge key", - "reference": "DOGE_KEY", - "key_type": "s", - } - ) - - # 1. Test server and partner specific key - server_partner_value = self.KeyValue.create( - { - "key_id": doge_key.id, - "secret_value": "Doge server and partner", - "server_id": self.server_test_1.id, - "partner_id": self.user_bob.partner_id.id, - } - ) - kwargs = { - "partner_id": self.user_bob.partner_id.id, - "server_id": self.server_test_1.id, - } - key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) - self.assertEqual( - key_value, "Doge server and partner", "Key value doesn't match" - ) - - # 2. Global key - doge_key.write({"secret_value": "Doge dog"}) - key_value = self.Key._resolve_key_type_secret("DOGE_KEY") - self.assertEqual(key_value, "Doge dog", "Key value doesn't match") - - # 3. Non existing key - key_value = self.Key._resolve_key_type_secret("PEPE_KEY") - self.assertIsNone(key_value, "Key value must be 'None'") - - # 4. Partner specific key - self.KeyValue.create( - { - "key_id": doge_key.id, - "secret_value": "Doge partner", - "partner_id": self.user_bob.partner_id.id, - } - ) - kwargs = { - "partner_id": self.user_bob.partner_id.id, - } - key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) - self.assertEqual(key_value, "Doge partner", "Key value doesn't match") - - # 5. Test server specific key - self.KeyValue.create( - { - "key_id": doge_key.id, - "secret_value": "Doge server", - "server_id": self.server_test_1.id, - } - ) - kwargs = { - "server_id": self.server_test_1.id, - } - key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) - self.assertEqual(key_value, "Doge server", "Key value doesn't match") - - # 6. Test with non matching partner. Should return server specific value - kwargs = { - "partner_id": self.user.partner_id.id, - "server_id": self.server_test_1.id, - } - key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) - self.assertEqual(key_value, "Doge server", "Key value doesn't match") - - # 7. Change partner in the server-partner specific value. - # Should return server specific value - server_partner_value.write({"partner_id": self.manager.partner_id.id}) - kwargs = { - "server_id": self.server_test_1.id, - } - key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) - self.assertEqual(key_value, "Doge server", "Key value doesn't match") - - # 8. Test with the global key again - key_value = self.Key._resolve_key_type_secret("DOGE_KEY") - self.assertEqual(key_value, "Doge dog", "Key value doesn't match") - - def test_parse_code(self): - """Test code parsing""" - - def check_parsed_code( - code, code_parsed_expected, expected_key_values=None, **kwargs - ): - """Helper function for code parse testing - - Args: - code (Text): code to parse - code_parsed_expected (Text): expected parsed code - expected_key_values (list, optional): key values that are expected - to be returned. Defaults to None. - """ - code_parsed = self.Key._parse_code(code, **kwargs) - self.assertEqual( - code_parsed, - code_parsed_expected, - msg="Parsed code doesn't match expected one", - ) - if expected_key_values: - result = self.Key._parse_code_and_return_key_values(code, **kwargs) - code_parsed = result["code"] - key_values = result["key_values"] - self.assertEqual( - code_parsed, - code_parsed_expected, - msg="Parsed code doesn't match expected one", - ) - self.assertEqual( - len(key_values), - len(expected_key_values), - "Number of key values doesn't match number of expected ones", - ) - for expected_value in expected_key_values: - self.assertIn( - expected_value, - key_values, - f"Value {expected_value} must be in the returned key values", - ) - - # Create new key - self.Key.create( - { - "name": "Meme key", - "reference": "MEME_KEY", - "secret_value": "Pepe Frog", - "key_type": "s", - } - ) - - # Check key parser - - # 1 - single line - - code = "The key to understand this meme is #!cxtower.secret.MEME_KEY!#" - code_parsed_expected = "The key to understand this meme is Pepe Frog" - expected_key_values = ["Pepe Frog"] - check_parsed_code(code, code_parsed_expected, expected_key_values) - - # 2 - multi line - code = "Welcome #!cxtower.secret.MEME_KEY!#\nNew hero of this city!" - code_parsed_expected = "Welcome Pepe Frog\nNew hero of this city!" - expected_key_values = ["Pepe Frog"] - check_parsed_code(code, code_parsed_expected, expected_key_values) - - # 3 - Key not found - code = "Don't mess with #!cxtower.secret.DOGE_LIKE!# He will make you cry" - code_parsed_expected = "Don't mess with None He will make you cry" - expected_key_values = [] - check_parsed_code(code, code_parsed_expected, expected_key_values) - - check_parsed_code(code, code_parsed_expected) - - # 4 - Multi keys - # Create new key - doge_key = self.Key.create( - { - "name": "doge key", - "reference": "DOGE_KEY", - "secret_value": "Doge dog", - "key_type": "s", - } - ) - code = ( - "Hey #!cxtower.secret.MEME_KEY!# & Doge #!cxtower.secret.DOGE_KEY !# so " - "like #!cxtower.secret.MEME_KEY!#!\n" - "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" - "cxtower.secret.DOGE_KEY" - ) - code_parsed_expected = ( - "Hey Pepe Frog & Doge Doge dog so " - "like Pepe Frog!\n" - "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" - "cxtower.secret.DOGE_KEY" - ) - expected_key_values = ["Pepe Frog", "Doge dog"] - check_parsed_code(code, code_parsed_expected, expected_key_values) - - # 5 - Partner specific key - # Create new key for partner Bob - self.KeyValue.create( - { - "key_id": doge_key.id, - "secret_value": "Doge wow", - "partner_id": self.user_bob.partner_id.id, - } - ) - # compose kwargs - kwargs = {"partner_id": self.user_bob.partner_id.id} - code_parsed_expected = ( - "Hey Pepe Frog & Doge Doge wow so " - "like Pepe Frog!\n" - "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" - "cxtower.secret.DOGE_KEY" - ) - expected_key_values = ["Pepe Frog", "Doge wow"] - check_parsed_code(code, code_parsed_expected, expected_key_values, **kwargs) - - # 6 - Server specific key - # Create new key for server Test 1 - self.KeyValue.create( - { - "key_id": doge_key.id, - "secret_value": "Doge much", - "server_id": self.server_test_1.id, - } - ) - # compose kwargs - kwargs = { - "partner_id": self.user_bob.partner_id.id, # not needed but may keep it - "server_id": self.server_test_1.id, - } - code_parsed_expected = ( - "Hey Pepe Frog & Doge Doge much so " - "like Pepe Frog!\n" - "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" - "cxtower.secret.DOGE_KEY" - ) - expected_key_values = ["Pepe Frog", "Doge much"] - check_parsed_code(code, code_parsed_expected, expected_key_values, **kwargs) - - def test_replace_with_spoiler(self): - """Check if secrets are replaced with spoiler correctly""" - - code = ( - "Hey Pepe Frog & Doge Doge much so " - "like Pepe Frog!\n" - "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" - "cxtower.secret.DOGE_KEY" - ) - placeholder = self.Key.SECRET_VALUE_PLACEHOLDER - expected_code = ( - f"Hey {placeholder} & Doge {placeholder} so " - f"like {placeholder}!\n" - "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" - "cxtower.secret.DOGE_KEY" - ) - key_values = ["Pepe Frog", "Doge much"] - - result = self.Key._replace_with_spoiler(code, key_values) - self.assertEqual(result, expected_code, "Result doesn't match expected code") - - # -------------------------------------- - # Check with some random key values now - # Original code should rename unchanged - # -------------------------------------- - - key_values = ["Wow much", "No like"] - result = self.Key._replace_with_spoiler(code, key_values) - self.assertEqual(result, code, "Result doesn't match expected code") - - def test_user_access(self): - """Test that regular users have no access to keys""" - user_key = self.Key.with_user(self.user) - - # Create test key - key = self.Key.create( - {"name": "Test Key", "secret_value": "test value", "key_type": "s"} - ) - - # Test CRUD operations - with self.assertRaises(AccessError): - user_key.create( - {"name": "New Key", "secret_value": "secret", "key_type": "s"} - ) - with self.assertRaises(AccessError): - user_key.browse(key.id).read(["name"]) - with self.assertRaises(AccessError): - user_key.browse(key.id).write({"name": "Updated Name"}) - with self.assertRaises(AccessError): - user_key.browse(key.id).unlink() - - def test_manager_read_access(self): - """Test manager read access rules""" - manager_key = self.Key.with_user(self.manager) - - # Create test keys - key_secret = self.Key.create( - {"name": "Secret Key", "secret_value": "secret value", "key_type": "s"} - ) - key_ssh = self.Key.create( - {"name": "SSH Key", "secret_value": "ssh key", "key_type": "k"} - ) - - # Test read access for secret key - should read (all managers can read secrets) - self.assertTrue(manager_key.search([("id", "=", key_secret.id)])) - - # Test read access for SSH key without server access - should not find - self.assertFalse(manager_key.search([("id", "=", key_ssh.id)])) - - # Add manager to server users and set SSH key - should find SSH key - self.write_and_invalidate( - self.server_1, - **{"user_ids": [(4, self.manager.id)], "ssh_key_id": key_ssh.id}, - ) - self.assertTrue(manager_key.search([("id", "=", key_ssh.id)])) - - # Remove key from server - should not find again - self.server_1.write({"ssh_key_id": False}) - self.assertFalse(manager_key.search([("id", "=", key_ssh.id)])) - - # Add as key user - should find both - key_secret.write({"user_ids": [(4, self.manager.id)]}) - key_ssh.write({"user_ids": [(4, self.manager.id)]}) - self.assertTrue(manager_key.search([("id", "=", key_secret.id)])) - self.assertTrue(manager_key.search([("id", "=", key_ssh.id)])) - - def test_manager_write_access(self): - """Test manager write/create access rules""" - manager_key = self.Key.with_user(self.manager) - - # Create test keys as root and ensure manager is not in manager_ids - key_secret = self.Key.create( - { - "name": "Secret Key", - "secret_value": "secret value", - "key_type": "s", - "manager_ids": [(5, 0)], # Clear manager_ids - } - ) - key_ssh = self.Key.create( - { - "name": "SSH Key", - "secret_value": "ssh key", - "key_type": "k", - "manager_ids": [(5, 0)], # Clear manager_ids - } - ) - - # Try write without being manager - should fail - with self.assertRaises(AccessError): - manager_key.browse(key_secret.id).write({"name": "Updated Secret"}) - with self.assertRaises(AccessError): - manager_key.browse(key_ssh.id).write({"name": "Updated SSH"}) - - # Add as key manager - should write to secret - key_secret.write({"manager_ids": [(4, self.manager.id)]}) - manager_key.browse(key_secret.id).write({"name": "Updated Secret"}) - self.assertEqual(key_secret.name, "Updated Secret") - - # Add as server manager and set SSH key - should write to SSH key - self.server_1.write( - {"manager_ids": [(4, self.manager.id)], "ssh_key_id": key_ssh.id} - ) - manager_key.browse(key_ssh.id).write({"name": "Updated SSH"}) - self.assertEqual(key_ssh.name, "Updated SSH") - - def test_manager_create_access(self): - """Test manager create access rules""" - manager_key = self.Key.with_user(self.manager) - manager_2_key = self.Key.with_user(self.manager_2) - - # Try create secret key when not a manager - should fail - with self.assertRaises(AccessError): - manager_2_key.create( - { - "name": "New Secret", - "secret_value": "secret", - "key_type": "s", - "manager_ids": [(5, 0)], # Prevent automatic manager addition - } - ) - - # Try create SSH key when not a server manager - should fail - with self.assertRaises(AccessError): - manager_2_key.create( - { - "name": "New SSH", - "secret_value": "ssh key", - "key_type": "k", - "manager_ids": [(5, 0)], # Prevent automatic manager addition - } - ) - - # Add as server manager - should create SSH key - self.server_1.write({"manager_ids": [(4, self.manager.id)]}) - new_ssh_key = manager_key.create( - {"name": "New SSH", "secret_value": "ssh key", "key_type": "k"} - ) - # Link key to server - self.server_1.write({"ssh_key_id": new_ssh_key.id}) - self.assertTrue(new_ssh_key.exists()) - - def test_manager_unlink_access(self): - """Test manager unlink access rules""" - manager_key = self.Key.with_user(self.manager) - - # Create keys as root - key_secret = self.Key.create( - {"name": "Secret Key", "secret_value": "secret value", "key_type": "s"} - ) - key_ssh = self.Key.create( - {"name": "SSH Key", "secret_value": "ssh key", "key_type": "k"} - ) - # Link SSH key to server - self.server_1.write({"ssh_key_id": key_ssh.id}) - - # Try delete without being manager and creator - should fail - with self.assertRaises(AccessError): - manager_key.browse(key_secret.id).unlink() - with self.assertRaises(AccessError): - manager_key.browse(key_ssh.id).unlink() - - # Add as manager but not creator - should still fail - key_secret.write({"manager_ids": [(4, self.manager.id)]}) - self.server_1.write({"manager_ids": [(4, self.manager.id)]}) - with self.assertRaises(AccessError): - manager_key.browse(key_secret.id).unlink() - with self.assertRaises(AccessError): - manager_key.browse(key_ssh.id).unlink() - - # Create own keys - should delete - own_secret = manager_key.create( - { - "name": "Own Secret", - "secret_value": "secret", - "key_type": "s", - "manager_ids": [(4, self.manager.id)], - } - ) - own_ssh = manager_key.create( - {"name": "Own SSH", "secret_value": "ssh key", "key_type": "k"} - ) - # Link own SSH key to server - self.server_1.write({"ssh_key_id": own_ssh.id}) - - own_secret.unlink() - own_ssh.unlink() - self.assertFalse(own_secret.exists()) - self.assertFalse(own_ssh.exists()) - - def test_root_access(self): - """Test root access rules""" - root_key = self.Key.with_user(self.root) - - # Create - key = root_key.create( - {"name": "Root Key", "secret_value": "root secret", "key_type": "s"} - ) - self.assertTrue(key.exists()) - - # Read - self.assertEqual(root_key.browse(key.id).name, "Root Key") - - # Write - root_key.browse(key.id).write({"name": "Updated Root Key"}) - self.assertEqual(key.name, "Updated Root Key") - - # Delete - key.unlink() - self.assertFalse(key.exists()) - - def test_key_value_user_access(self): - """Test that regular users have no access to key values""" - user_key_value = self.KeyValue.with_user(self.user) - - # Create test key and key value - key = self.Key.create({"name": "Test Key", "key_type": "s"}) - key_value = self.KeyValue.create( - {"key_id": key.id, "secret_value": "test value"} - ) - - # Test CRUD operations - with self.assertRaises(AccessError): - user_key_value.create({"key_id": key.id, "secret_value": "new value"}) - with self.assertRaises(AccessError): - user_key_value.browse(key_value.id).read(["secret_value"]) - with self.assertRaises(AccessError): - user_key_value.browse(key_value.id).write({"secret_value": "updated value"}) - with self.assertRaises(AccessError): - user_key_value.browse(key_value.id).unlink() - - def test_key_value_manager_read_access(self): - """Test manager read access rules for key values""" - manager_key_value = self.KeyValue.with_user(self.manager) - - # Create test key and key values - key = self.Key.create({"name": "Test Key", "key_type": "s"}) - global_value = self.KeyValue.create( - {"key_id": key.id, "secret_value": "global value"} - ) - server_value = self.KeyValue.create( - { - "key_id": key.id, - "secret_value": "server value", - "server_id": self.server_1.id, - } - ) - - # Test read access - should not find without proper access - self.assertTrue(manager_key_value.search([("id", "=", global_value.id)])) - self.assertFalse(manager_key_value.search([("id", "=", server_value.id)])) - - # Add as key user - should find global value and server value for that key - key.write({"user_ids": [(4, self.manager.id)]}) - self.assertTrue(manager_key_value.search([("id", "=", global_value.id)])) - self.assertTrue(manager_key_value.search([("id", "=", server_value.id)])) - - # Remove from key users - key.write({"user_ids": [(3, self.manager.id)]}) - self.assertTrue(manager_key_value.search([("id", "=", global_value.id)])) - self.assertFalse(manager_key_value.search([("id", "=", server_value.id)])) - - # Add as server user - should find server value - self.server_1.write({"user_ids": [(4, self.manager.id)]}) - self.assertTrue(manager_key_value.search([("id", "=", global_value.id)])) - self.assertTrue(manager_key_value.search([("id", "=", server_value.id)])) - - def test_key_value_manager_write_access(self): - """Test manager write/create access rules for key values""" - manager_key_value = self.KeyValue.with_user(self.manager) - - # Create test key and key values - key = self.Key.create({"name": "Test Key", "key_type": "s"}) - global_value = self.KeyValue.create( - {"key_id": key.id, "secret_value": "global value"} - ) - server_value = self.KeyValue.create( - { - "key_id": key.id, - "secret_value": "server value", - "server_id": self.server_1.id, - } - ) - - # Try write without proper access - should fail - with self.assertRaises(AccessError): - manager_key_value.browse(global_value.id).write( - {"secret_value": "new value"} - ) - with self.assertRaises(AccessError): - manager_key_value.browse(server_value.id).write( - {"secret_value": "new value"} - ) - - # Add as key manager - should write to global value - key.write({"manager_ids": [(4, self.manager.id)]}) - manager_key_value.browse(global_value.id).write( - {"secret_value": "updated global"} - ) - self.assertEqual( - global_value._get_secret_value("secret_value"), "updated global" - ) - - # Add as server manager - should write to server value - self.server_1.write({"manager_ids": [(4, self.manager.id)]}) - manager_key_value.browse(server_value.id).write( - {"secret_value": "updated server"} - ) - self.assertEqual( - server_value._get_secret_value("secret_value"), "updated server" - ) - - # Test create access - for_bob = manager_key_value.create( - { - "key_id": key.id, - "secret_value": "for bob", - "partner_id": self.user_bob.partner_id.id, - } - ) - self.assertTrue(for_bob.exists()) - - def test_key_value_manager_unlink_access(self): - """Test manager unlink access rules for key values""" - manager_key_value = self.KeyValue.with_user(self.manager) - - # Create test key and key values - key = self.Key.create({"name": "Test Key", "key_type": "s"}) - - # Create values as root - global_value = self.KeyValue.create( - {"key_id": key.id, "secret_value": "global value"} - ) - server_value = self.KeyValue.create( - { - "key_id": key.id, - "secret_value": "server value", - "server_id": self.server_1.id, - } - ) - - # Try delete without proper access - should fail - with self.assertRaises(AccessError): - manager_key_value.browse(global_value.id).unlink() - with self.assertRaises(AccessError): - manager_key_value.browse(server_value.id).unlink() - - # Add as manager but not creator - should still fail - key.write({"manager_ids": [(4, self.manager.id)]}) - self.server_1.write({"manager_ids": [(4, self.manager.id)]}) - with self.assertRaises(AccessError): - manager_key_value.browse(global_value.id).unlink() - with self.assertRaises(AccessError): - manager_key_value.browse(server_value.id).unlink() - - # Create own values - should delete - own_partner_value = manager_key_value.create( - { - "key_id": key.id, - "secret_value": "own partner", - "partner_id": self.user_bob.partner_id.id, - } - ) - - # Unlink server value first to avoid constraint error - server_value.unlink() - - # Create server value - own_server_value = manager_key_value.create( - { - "key_id": key.id, - "secret_value": "own server", - "server_id": self.server_1.id, - } - ) - - own_partner_value.unlink() - own_server_value.unlink() - self.assertFalse(own_partner_value.exists()) - self.assertFalse(own_server_value.exists()) - - def test_key_value_root_access(self): - """Test root access rules for key values""" - root_key_value = self.KeyValue.with_user(self.root) - - # Create test key - key = self.Key.create({"name": "Test Key", "key_type": "s"}) - - # Create - value = root_key_value.create({"key_id": key.id, "secret_value": "root value"}) - self.assertTrue(value.exists()) - - # Read - self.assertEqual( - root_key_value.browse(value.id)._get_secret_value("secret_value"), - "root value", - ) - - # Write - root_key_value.browse(value.id).write({"secret_value": "updated value"}) - self.assertEqual(value._get_secret_value("secret_value"), "updated value") - - # Delete - value.unlink() - self.assertFalse(value.exists()) - - def test_key_value_global_unique(self): - """Test global value uniqueness""" - - # Try to create a value for the same key - with self.assertRaises(ValidationError): - another_global_value = self.KeyValue.create( - {"key_id": self.test_key.id, "secret_value": "another test value"} - ) - # - another_global_value.unlink() - - def test_key_value_server_unique(self): - """Test server value uniqueness""" - # Create server tight value - - self.KeyValue.create( - { - "key_id": self.test_key.id, - "secret_value": "server related", - "server_id": self.server_1.id, - } - ) - - # Try create another value for the same server - with self.assertRaises(ValidationError): - self.KeyValue.create( - { - "key_id": self.test_key.id, - "secret_value": "another server related", - "server_id": self.server_1.id, - } - ) - - def test_key_value_partner_unique(self): - """Test partner value uniqueness""" - # Create partner tight value - self.KeyValue.create( - { - "key_id": self.test_key.id, - "secret_value": "partner related", - "partner_id": self.user_bob.partner_id.id, - } - ) - - # Try create another value for the same partner - with self.assertRaises(ValidationError): - self.KeyValue.create( - { - "key_id": self.test_key.id, - "secret_value": "another partner related", - "partner_id": self.user_bob.partner_id.id, - } - ) - - def test_key_value_server_partner_unique(self): - """Test server and partner value uniqueness""" - - # Create server and partner tight value - self.KeyValue.create( - { - "key_id": self.test_key.id, - "secret_value": "server related", - "server_id": self.server_1.id, - "partner_id": self.user_bob.partner_id.id, - } - ) - - # Try create another value for the same server and partner - with self.assertRaises(ValidationError): - self.KeyValue.create( - { - "key_id": self.test_key.id, - "secret_value": "another server related", - "server_id": self.server_1.id, - "partner_id": self.user_bob.partner_id.id, - } - ) diff --git a/addons/cetmix_tower_server/tests/test_partner_server_btn.py b/addons/cetmix_tower_server/tests/test_partner_server_btn.py deleted file mode 100644 index fcfb2dd..0000000 --- a/addons/cetmix_tower_server/tests/test_partner_server_btn.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.tests.common import tagged - -from .common import TestTowerCommon - - -@tagged("partner_servers_btn") -class TestPartnerServers(TestTowerCommon): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.partner_a = cls.env["res.partner"].create({"name": "Partner A"}) - cls.partner_b = cls.env["res.partner"].create({"name": "Partner B"}) - cls.partner_b_child = cls.env["res.partner"].create( - { - "name": "Partner B Child", - "parent_id": cls.partner_b.id, - } - ) - - cls.server_defaults = { - "name": "Test Server", - "ssh_username": "root", - "ssh_port": 22, - "ssh_password": "Test-P@ssw0rd-123", - "ip_v4_address": "127.0.0.1", - "skip_host_key": True, - } - - cls.Server.create({"partner_id": cls.partner_b.id, **cls.server_defaults}) - cls.Server.create({"partner_id": cls.partner_b.id, **cls.server_defaults}) - cls.Server.create({"partner_id": cls.partner_b_child.id, **cls.server_defaults}) - - key = cls.Key.create({"name": "SSH Token", "key_type": "s"}) - cls.KeyValue.create( - { - "key_id": key.id, - "partner_id": cls.partner_b.id, - "secret_value": "TOPSECRET", - } - ) - - def test_server_count_compute(self): - """Server count: direct + one‑level child + zero if none.""" - self.assertEqual(self.partner_b.server_count, 3) - self.assertEqual(self.partner_b_child.server_count, 1) - self.assertEqual(self.partner_a.server_count, 0) - - def test_parent_with_only_child_servers(self): - """Parent without servers directs and with child_of.""" - parent = self.env["res.partner"].create({"name": "Parent Only"}) - child = self.env["res.partner"].create( - {"name": "Child with Server", "parent_id": parent.id} - ) - self.Server.create({"partner_id": child.id, **self.server_defaults}) - self.assertEqual(parent.server_count, 1) diff --git a/addons/cetmix_tower_server/tests/test_plan.py b/addons/cetmix_tower_server/tests/test_plan.py deleted file mode 100644 index aa4eb0a..0000000 --- a/addons/cetmix_tower_server/tests/test_plan.py +++ /dev/null @@ -1,2639 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from unittest.mock import patch - -from odoo import _, fields -from odoo.exceptions import AccessError, ValidationError -from odoo.tools.misc import mute_logger - -from ..models.constants import ( - ANOTHER_PLAN_RUNNING, - GENERAL_ERROR, - PLAN_IS_EMPTY, - PLAN_LINE_CONDITION_CHECK_FAILED, - PLAN_NOT_COMPATIBLE_WITH_SERVER, - PLAN_STOPPED, -) -from .common import TestTowerCommon - - -class TestTowerPlan(TestTowerCommon): - """Test the cx.tower.plan model.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Commands - cls.command_run_flight_plan_1 = cls.Command.create( - { - "name": "Run Flight Plan", - "action": "plan", - "flight_plan_id": cls.plan_1.id, - } - ) - cls.command_python_custom_variable_values_1 = cls.Command.create( - { - "name": "Python command to set custom variable values", - "action": "python_code", - "code": """ -custom_values['test_path_'] = '/test_path' -custom_values['test_dir'] = 'test_dir' -custom_values['_my_value'] = 'Just To Test' -""", - } - ) - cls.command_python_custom_variable_values_2 = cls.Command.create( - { - "name": "Python command to update custom variable values", - "action": "python_code", - "code": f""" -custom_values['test_path_'] = '/another_test_path' -custom_values['random_var_reference'] = 'random_var_value' -custom_values['{cls.variable_url.reference}'] = 'https://www.cetmix.com' -""", - } - ) - # Flight plan - cls.plan_2 = cls.Plan.create( - { - "name": "Test plan 2", - "note": "Run another flight plan", - } - ) - cls.plan_2_line_1 = cls.plan_line.create( - { - "sequence": 5, - "plan_id": cls.plan_2.id, - "command_id": cls.command_run_flight_plan_1.id, - } - ) - cls.plan_2_line_2 = cls.plan_line.create( - { - "sequence": 10, - "command_id": cls.command_create_dir.id, - } - ) - # Flight plan with access level 1 to test user access rights - cls.plan_3 = cls.Plan.create( - { - "name": "Test plan 3", - "note": "Test user access rights", - "access_level": "1", - "line_ids": [ - (0, 0, {"command_id": cls.command_create_dir.id, "sequence": 1}), - ], - } - ) - # Create line for plan 3 - cls.plan_3_line_1 = cls.plan_line.create( - { - "plan_id": cls.plan_3.id, - "command_id": cls.command_create_dir.id, - "sequence": 10, - } - ) - cls.plan_3_line_1_action = cls.env["cx.tower.plan.line.action"].create( - { - "line_id": cls.plan_3_line_1.id, - "condition": "==", - "value_char": "test", - "action": "e", - } - ) - cls.variable_value = cls.env["cx.tower.variable.value"].create( - { - "variable_id": cls.variable_os.id, - "value_char": "Windows 2k", - "plan_line_action_id": cls.plan_3_line_1_action.id, - } - ) - cls.server = cls.Server.create( - { - "name": "Plan Test Server", - "ssh_username": "test", - "ssh_password": "test", - "ip_v4_address": "localhost", - "ssh_port": 22, - "user_ids": [(6, 0, [cls.user.id])], - "manager_ids": [(6, 0, [cls.manager.id])], - "skip_host_key": True, - } - ) - - def _create_plan(self, **kwargs): - """Helper method to create a flight plan.""" - vals = { - "name": "Test Flight Plan", - "access_level": "1", # override default for user tests - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - "server_ids": [(6, 0, [])], - } - if kwargs: - vals.update(kwargs) - return self.Plan.create(vals) - - def test_user_read_access(self): - """ - For a user: - Read access is allowed if access_level == "1" and - either the plan's own user_ids includes the user - OR at least one related server (via server_ids) - includes the user in its user_ids. - """ - # Case 1: Plan with access_level "1" and user - # included in plan.user_ids. - plan1 = self._create_plan( - **{ - "access_level": "1", - "user_ids": [(6, 0, [self.user.id])], - } - ) - recs1 = self.Plan.with_user(self.user).search([("id", "=", plan1.id)]) - self.assertIn( - plan1, - recs1, - "User should see the plan if in " "plan.user_ids and access_level == '1'.", - ) - - # Case 2: Plan with access_level "1" with no direct user_ids, - # but with a related server that grants access. - plan2 = self._create_plan( - **{ - "access_level": "1", - "user_ids": [(6, 0, [])], - "server_ids": [(6, 0, [self.server.id])], - } - ) - recs2 = self.Plan.with_user(self.user).search([("id", "=", plan2.id)]) - self.assertIn( - plan2, - recs2, - "User should see the plan if a " - "related server.user_ids includes the user.", - ) - - # Negative: Plan with access_level "1" - # with neither direct nor server-based access. - plan3 = self._create_plan( - **{ - "access_level": "1", - "user_ids": [(6, 0, [])], - "server_ids": [(6, 0, [])], - } - ) - recs3 = self.Plan.with_user(self.user).search([("id", "=", plan3.id)]) - self.assertNotIn( - plan3, - recs3, - "User should not see the plan if not granted access.", - ) - - # Also, a user should not be allowed to create a plan. - with self.assertRaises(AccessError): - self.Plan.with_user(self.user).create( - { - "name": "Test Plan", - "access_level": "1", - "user_ids": [(6, 0, [self.user.id])], - } - ) - # ...and modify a plan that they have access to. - with self.assertRaises(AccessError): - plan1.with_user(self.user).write({"name": "User Updated Plan"}) - - def test_manager_read_access(self): - """ - For a manager: - Read access is allowed if access_level <= "2" AND - EITHER the plan itself grants access - (its user_ids or manager_ids includes the manager) - OR either there are no related servers OR a related server - grants access (its user_ids or manager_ids includes the manager). - """ - # Case 1: Plan with access_level "2" and plan.manager_ids - # includes the manager. - plan1 = self._create_plan( - **{ - "access_level": "2", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - recs1 = self.Plan.with_user(self.manager).search([("id", "=", plan1.id)]) - self.assertIn( - plan1, - recs1, - "Manager should see the plan if in " - "plan.manager_ids and access_level <= '2'.", - ) - - # Case 2: Plan with access_level "2" that does not grant direct access, - # but a related server grants access via its manager_ids. - plan2 = self._create_plan( - **{ - "access_level": "2", - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - "server_ids": [(6, 0, [self.server.id])], - } - ) - recs2 = self.Plan.with_user(self.manager).search([("id", "=", plan2.id)]) - self.assertIn( - plan2, - recs2, - "Manager should see the plan if related " - "server.manager_ids includes the manager.", - ) - - # Case 3 negative: Plan with access_level "2" with no granted access - # if it's linked to a server that does not grant access. - plan3 = self._create_plan( - **{ - "access_level": "2", - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - recs3 = self.Plan.with_user(self.manager).search([("id", "=", plan3.id)]) - self.assertNotIn( - plan3, - recs3, - "Manager should not see the plan " - "if not granted access to related server.", - ) - - # Case 4 positive: Plan with access_level "2" with no linked servers - # and no related servers that grant access. - plan4 = self._create_plan( - **{ - "access_level": "2", - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - "server_ids": [(6, 0, [])], - } - ) - recs4 = self.Plan.with_user(self.manager).search([("id", "=", plan4.id)]) - self.assertIn( - plan4, - recs4, - "Manager should see the plan if not linked to any servers.", - ) - - # Case 5 negative: raise access level to 3 - # and check if manager can see the plan - plan4.access_level = "3" - recs5 = self.Plan.with_user(self.manager).search([("id", "=", plan4.id)]) - self.assertNotIn( - plan4, - recs5, - "Manager should not see the plan " "if access level is raised to 3.", - ) - - def test_manager_write_create_access(self): - """ - For a manager: - Write (update) and create access are allowed if access_level <= "2" AND - the plan's own manager_ids includes the manager. - """ - # Case 1: Plan with access_level "2" and plan.manager_ids - # includes the manager should allow to update the plan. - plan1 = self._create_plan( - **{ - "access_level": "2", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - try: - plan1.with_user(self.manager).write({"name": "Manager Updated Plan"}) - except AccessError: - self.fail( - "Manager should be able to update the plan if " "in plan.manager_ids.", - ) - self.assertEqual( - plan1.with_user(self.manager).name, - "Manager Updated Plan", - ) - - # Case 2: Attempt to create a plan as a manager without - # including their ID in manager_ids should fail. - with self.assertRaises(AccessError): - self.Plan.with_user(self.manager).create( - { - "name": "Manager Created Plan", - "access_level": "2", - "manager_ids": [(6, 0, [])], - } - ) - - # Case 3: Create a plan with manager added to manager_ids - # should be allowed. - try: - self.Plan.with_user(self.manager).create( - { - "name": "Manager Created Plan", - "access_level": "2", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - except AccessError: - self.fail( - "Manager should be able to create a plan " - "with himself added to manager_ids.", - ) - - def test_manager_unlink_access(self): - """ - For a manager: - Unlink (delete) access is allowed if access_level <= "2", - the current user is the record creator, - AND the plan's own manager_ids includes the manager. - """ - # Scenario 1: Plan created by the manager with plan.manager_ids - # including the manager. - plan1 = self.Plan.with_user(self.manager).create( - { - "name": "Manager Created Plan", - "access_level": "2", - } - ) - try: - plan1.unlink() - except AccessError: - self.fail( - "Manager should be able to delete the plan " - "they created if in plan.manager_ids.", - ) - - # Scenario 2: Plan created by another user, even if - # plan.manager_ids includes the manager. - plan2 = self._create_plan( - **{ - "access_level": "2", - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - with self.assertRaises(AccessError): - plan2.with_user(self.manager).unlink() - - def test_root_unrestricted_access(self): - """ - For a root user: - Unlimited access: root can read, write, create, and delete plans - regardless of access_level or related servers. - """ - plan = self._create_plan( - **{ - "access_level": "3", # above threshold for managers - } - ) - recs = self.Plan.with_user(self.root).search([("id", "=", plan.id)]) - self.assertIn( - plan, - recs, - "Root should see the plan regardless of restrictions.", - ) - try: - plan.with_user(self.root).write({"name": "Root Updated Plan"}) - except AccessError: - self.fail("Root should be able to update the plan without restrictions.") - self.assertEqual(plan.with_user(self.root).name, "Root Updated Plan") - plan2 = self.Plan.with_user(self.root).create( - { - "name": "Root Created Plan", - "access_level": "3", - } - ) - self.assertTrue( - plan2, - "Root should be able to create a plan without restrictions.", - ) - plan2.with_user(self.root).unlink() - recs_after = self.Plan.with_user(self.root).search([("id", "=", plan2.id)]) - self.assertFalse( - recs_after, - "Root should be able to delete the plan without restrictions.", - ) - - def test_plan_line_action_name(self): - """Test plan line action naming""" - - # Add new line - plan_line_1 = self.plan_line.create( - { - "plan_id": self.plan_1.id, - "command_id": self.command_create_dir.id, - "sequence": 10, - } - ) - - # Add new action with custom - action_1 = self.plan_line_action.create( - { - "line_id": plan_line_1.id, - "condition": "==", - "value_char": "35", - "action": "e", - } - ) - - # Check if action name is composed correctly - expected_action_string = _( - "If exit code == 35 then Exit with command exit code" - ) - self.assertEqual( - action_1.name, - expected_action_string, - msg="Action name doesn't match expected one", - ) - - def test_plan_get_next_action_values(self): - """Test _get_next_action_values() - - NB: This test relies on demo data and might fail if it is modified - """ - # Ensure demo date integrity just in case demo date is modified - self.assertEqual( - self.plan_1.line_ids[0].action_ids[1].custom_exit_code, - 255, - "Plan 1 line #1 action #2 custom exit code must be equal to 255", - ) - - # Create a new plan log. - plan_line_1 = self.plan_1.line_ids[0] # Using command 1 from Plan 1 - plan_log = self.PlanLog.create( - { - "server_id": self.server_test_1.id, - "plan_id": self.plan_1.id, - "is_running": True, - "start_date": fields.Datetime.now(), - "plan_line_executed_id": plan_line_1.id, - } - ) - - # ************************ - # Test with exit code == 0 - # Must run the next command - # ************************ - command_log = self.CommandLog.create( - { - "plan_log_id": plan_log.id, - "server_id": self.server_test_1.id, - "command_id": plan_line_1.command_id.id, - "command_response": "Ok", - "command_status": 0, # Error code - } - ) - action, exit_code, next_line_id = self.plan_1._get_next_action_values( - command_log - ) - self.assertEqual(action, "n", msg="Action must be 'Run next action'") - self.assertEqual(exit_code, 0, msg="Exit code must be equal to 0") - self.assertEqual( - next_line_id, - self.plan_line_2, - msg="Next line must be Line #2", - ) - - # ************************ - # Test with exit code == 8 - # Must exit with custom code - # ************************ - command_log.command_status = 8 - - action, exit_code, next_line_id = self.plan_1._get_next_action_values( - command_log - ) - self.assertEqual(action, "ec", msg="Action must be 'Exit with custom code'") - self.assertEqual(exit_code, 255, msg="Exit code must be equal to 255") - self.assertIsNone(next_line_id, msg="Next line must be None") - - # ************************ - # Test with exit code == -12 - # Plan on error action must be triggered because no action condition is matched - # ************************ - command_log.command_status = -12 - - action, exit_code, next_line_id = self.plan_1._get_next_action_values( - command_log - ) - self.assertEqual(action, "e", msg="Action must be 'Exit with command code'") - self.assertEqual(exit_code, -12, msg="Exit code must be equal to -12") - self.assertIsNone(next_line_id, msg="Next line must be None") - - # ************************ - # Change Plan 'On error action' of the plan to 'Run next command' - # Next line must be Line #2 - # ************************ - - command_log.command_status = -12 - self.plan_1.on_error_action = "n" - - action, exit_code, next_line_id = self.plan_1._get_next_action_values( - command_log - ) - self.assertEqual(action, "n", msg="Action must be 'Run next action'") - self.assertEqual(exit_code, -12, msg="Exit code must be equal to -12") - self.assertEqual( - next_line_id, - self.plan_line_2, - msg="Next line must be Line #2", - ) - - # ************************ - # Run Line 2 (the last one). - # Action 2 will be triggered which is "Run next line". - # However because this is the last line of the plan must exit with command code. - # ************************ - - plan_line_2 = self.plan_1.line_ids[1] - plan_log.plan_line_executed_id = plan_line_2.id - command_log.command_status = 3 - - action, exit_code, next_line_id = self.plan_1._get_next_action_values( - command_log - ) - self.assertEqual(action, "e", msg="Action must be 'Exit with command code'") - self.assertEqual(exit_code, 3, msg="Exit code must be equal to 3") - self.assertIsNone(next_line_id, msg="Next line must be None") - - # ************************ - # Run Line 2 (the last one). - # Fallback plan action must be triggered because no action condition is matched - # However because this is the last line of the plan must exit with command code. - # ************************ - - command_log.command_status = 1 - - action, exit_code, next_line_id = self.plan_1._get_next_action_values( - command_log - ) - self.assertEqual(action, "e", msg="Action must be 'Exit with command code'") - self.assertEqual(exit_code, 1, msg="Exit code must be equal to 1") - self.assertIsNone(next_line_id, msg="Next line must be None") - - def test_plan_run_single(self): - """Test plan execution results""" - - # Add user as user to Server1 - self.server_test_1.user_ids = [(4, self.user_bob.id)] - - # Ensure that access error is raised - # Because user_bob is not in any Tower group - with self.assertRaises(AccessError): - self.plan_1.with_user(self.user_bob)._run_single(self.server_test_1) - - # Add user to the "User" group - self.add_to_group(self.user_bob, "cetmix_tower_server.group_user") - - # Ensure that access error is raised - # Because plan access level is "Manager" and user_bob is in "User" group - with self.assertRaises(AccessError): - self.plan_1.with_user(self.user_bob)._run_single(self.server_test_1) - - # Set access level to 1 and link to server1 - # so Bob can execute the plan - self.write_and_invalidate( - self.plan_1, - **{"access_level": "1", "server_ids": [(4, self.server_test_1.id)]}, - ) - - self.env["ir.rule"].invalidate_model() - # Run plan - self.plan_1.with_user(self.user_bob)._run_single(self.server_test_1) - - # Check plan log - plan_log_rec = self.PlanLog.search([("server_id", "=", self.server_test_1.id)]) - - # Must be a single record - self.assertEqual(len(plan_log_rec), 1, msg="Must be a single plan record") - - # Ensure all commands were triggered - expected_command_count = 2 - self.assertEqual( - len(plan_log_rec.command_log_ids), - expected_command_count, - msg=f"Must run {expected_command_count} commands", - ) - - # Check plan status - expected_plan_status = 0 - self.assertEqual( - plan_log_rec.plan_status, - expected_plan_status, - msg=f"Plan status must be equal to {expected_plan_status}", - ) - - # ************************ - # Change condition in line #1. - # Action 1 will be triggered which is "Exit with custom code" 29. - # ************************ - action_to_tweak = self.plan_line_1_action_1 - action_to_tweak.write({"custom_exit_code": 29, "action": "ec"}) - - # Run plan - self.plan_1._run_single(self.server_test_1) - - # Check plan log - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - - # Must be two plan log record - self.assertEqual(len(plan_log_records), 2, msg="Must be 2 plan log records") - plan_log_rec = plan_log_records[0] - - # Ensure all commands were triggered - expected_command_count = 1 - self.assertEqual( - len(plan_log_rec.command_log_ids), - expected_command_count, - msg=f"Must run {expected_command_count} commands", - ) - - # Check plan status - expected_plan_status = 29 - self.assertEqual( - plan_log_rec.plan_status, - expected_plan_status, - msg=f"Plan status must be equal to {expected_plan_status}", - ) - - # Ensure 'path' was substituted with the plan line custom 'path' - self.assertEqual( - self.plan_line_1.path, - plan_log_rec.command_log_ids.path, - "Path in command log must be the same as in the flight plan line", - ) - - def test_plan_and_command_access_level(self): - # Remove userbob from all cxtower_server groups - self.remove_from_group( - self.user_bob, - [ - "cetmix_tower_server.group_user", - "cetmix_tower_server.group_manager", - "cetmix_tower_server.group_root", - ], - ) - - # Add user_bob to group_manager - self.add_to_group(self.user_bob, "cetmix_tower_server.group_manager") - - # Add user_bob as manager to the plan - self.plan_1.manager_ids = [(4, self.user_bob.id)] - - # check if plan and commands included has same access level - self.assertEqual(self.plan_1.access_level, "2") - self.assertEqual(self.command_create_dir.access_level, "2") - self.assertEqual(self.command_list_dir.access_level, "2") - - # check that if we modify plan access level to make it lower than the - # access_level of the commands related with it access level, - # access_level_warn_msg will be created - self.plan_1.with_user(self.user_bob).write({"access_level": "1"}) - self.assertTrue(self.plan_1.access_level_warn_msg) - - # Add user_bob to group_root - self.add_to_group(self.user_bob, "cetmix_tower_server.group_root") - - # check if user_bob can make plan access leve higher than commands access level - self.plan_1.with_user(self.user_bob).write({"access_level": "3"}) - self.assertEqual(self.plan_1.access_level, "3") - - # check that if we create a new plan with an access_level lower than - # the access_level of the command related with access_level_warn_msg - # will be created - command_1 = self.Command.create( - {"name": "New Test Command", "access_level": "3"} - ) - - self.plan_2 = self.Plan.create( - { - "name": "Test plan 2", - "note": "Create directory and list its content", - } - ) - self.plan_line_2_1 = self.plan_line.create( - { - "sequence": 5, - "plan_id": self.plan_2.id, - "command_id": command_1.id, - } - ) - self.assertTrue(self.plan_2.access_level_warn_msg) - - def test_multiple_plan_create_write(self): - """Test multiple plan create/write cases""" - # Create multiple plans at once - plans_data = [ - { - "name": "Test Plan 1", - "note": "Plan 1 Note", - "tag_ids": [(6, 0, [self.tag_test_staging.id])], - }, - { - "name": "Test Plan 2", - "note": "Plan 2 Note", - "tag_ids": [(6, 0, [self.tag_test_production.id])], - }, - { - "name": "Test Plan 3", - "note": "Plan 3 Note", - "tag_ids": [(6, 0, [self.tag_test_staging.id])], - }, - ] - created_plans = self.Plan.create(plans_data) - # Check that all plans are created successfully - self.assertTrue(all(created_plans)) - # Update the access level of the created plans - created_plans.write({"access_level": "3"}) - # Check that all plans are updated successfully - self.assertTrue(all(plan.access_level == "3" for plan in created_plans)) - - def test_plan_with_first_not_executable_condition(self): - """ - Test plan with not executable condition for first plan line - """ - # Add condition for the first plan line - self.plan_line_1.condition = "{{ odoo_version }} == '14.0'" - # Run plan - self.plan_1._run_single(self.server_test_1) - # Check plan log - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual( - len(plan_log_records.command_log_ids), - 2, - msg="Must be two command records", - ) - self.assertTrue( - plan_log_records.command_log_ids[0].is_skipped, - msg="First command must be skipped", - ) - self.assertFalse( - plan_log_records.command_log_ids[1].is_skipped, - msg="Second command not must be skipped", - ) - - def test_plan_with_second_not_executable_condition(self): - """ - Test plan with not executable condition for second plan line - """ - # Add condition for second plan line - self.plan_line_2.condition = "{{ odoo_version }} == '14.0'" - # Run plan - self.plan_1._run_single(self.server_test_1) - # Check plan log - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual( - len(plan_log_records.command_log_ids), - 2, - msg="Must be two command records", - ) - self.assertTrue( - plan_log_records.command_log_ids[1].is_skipped, - msg="Second command must be skipped", - ) - self.assertFalse( - plan_log_records.command_log_ids[0].is_skipped, - msg="First command not must be skipped", - ) - - def test_plan_with_executable_condition(self): - """ - Test plan with executable condition for plan line - """ - # Add condition for first plan line - self.plan_line_1.condition = "1 == 1" - # Create a global value for the 'Version' variable - self.VariableValue.create( - {"variable_id": self.variable_version.id, "value_char": "14.0"} - ) - # Add condition with variable - self.plan_line_2.condition = ( - "{{ " + self.variable_version.name + " }} == '14.0'" - ) - # Run plan - self.plan_1._run_single(self.server_test_1) - # Check commands - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual( - len(plan_log_records.command_log_ids), - 2, - msg="Must be two command records", - ) - self.assertTrue( - all(not command.is_skipped for command in plan_log_records.command_log_ids), - msg="All command should be executed", - ) - - def test_plan_with_update_variables(self): - """ - Test plan updates custom (in-flight) values - """ - # Add new variable to server - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "value_char": "14.0", - "server_id": self.server_test_1.id, - } - ) - # Create new variable value on action - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "value_char": "16.0", - "plan_line_action_id": self.plan_line_1_action_1.id, - } - ) - # Add a new variable value on action for a variable absent on the server - self.VariableValue.create( - { - "variable_id": self.variable_os.id, - "value_char": "Ubuntu", - "plan_line_action_id": self.plan_line_1_action_1.id, - } - ) - # Pre-run sanity: server holds initial value and no OS value - exist_server_values = self.server_test_1.variable_value_ids.filtered( - lambda rec: rec.variable_id == self.variable_version - ) - self.assertEqual( - len(exist_server_values), - 1, - "The server should have only one value for the variable", - ) - self.assertEqual( - exist_server_values.value_char, - "14.0", - "The server variable value should be '14.0'", - ) - exist_server_values = self.server_test_1.variable_value_ids.filtered( - lambda rec: rec.variable_id == self.variable_os - ) - self.assertFalse( - exist_server_values, "The server should not have this variable" - ) - # Run plan - self.plan_1._run_single(self.server_test_1) - # After run: server values MUST remain unchanged - server_version_val = self.server_test_1.variable_value_ids.filtered( - lambda rec: rec.variable_id == self.variable_version - ) - self.assertEqual( - server_version_val.value_char, - "14.0", - "Server variable value must remain unchanged", - ) - self.assertFalse( - self.server_test_1.variable_value_ids.filtered( - lambda rec: rec.variable_id == self.variable_os - ), - "Server must not receive new variable from action", - ) - - # But custom (in-flight) values MUST be updated in logs - plan_log = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)], order="id desc", limit=1 - ) - self.assertTrue(plan_log, "Plan log should exist after run") - self.assertEqual( - plan_log.variable_values[self.variable_version.reference], - "16.0", - "Plan log must contain updated custom value", - ) - self.assertEqual( - plan_log.variable_values[self.variable_os.reference], - "Ubuntu", - "Plan log must contain new custom value", - ) - - last_command_log = plan_log.command_log_ids and plan_log.command_log_ids[-1] - self.assertTrue(last_command_log, "Command log should exist after run") - self.assertEqual( - last_command_log.variable_values[self.variable_version.reference], - "16.0", - "Command log must contain updated custom value", - ) - - def test_plan_with_action_variables_for_condition(self): - """ - Test plan with update server variables and use new - value as condition for next plan line - """ - # Add new variable to server - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "value_char": "14.0", - "server_id": self.server_test_1.id, - } - ) - # Create new variable value to action to update existing server variable - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "value_char": "16.0", - "plan_line_action_id": self.plan_line_1_action_1.id, - } - ) - # Add condition with variable - self.plan_line_2.condition = ( - "{{ " + self.variable_version.name + " }} == '14.0'" - ) - # Run plan - self.plan_1._run_single(self.server_test_1) - # Check commands - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - # The second line of the plan should be skipped because the - # first line of the plan updated the value of the variable - self.assertTrue( - plan_log_records.command_log_ids[1].is_skipped, - msg="Second command must be skipped", - ) - - # Change condition for plan line - self.plan_line_2.condition = ( - "{{ " + self.variable_version.name + " }} == '16.0'" - ) - # Run plan - self.plan_1._run_single(self.server_test_1) - # Check commands - new_plan_log_records = ( - self.PlanLog.search([("server_id", "=", self.server_test_1.id)]) - - plan_log_records - ) - # The second line of the plan should be skipped because the - # first line of the plan updated the value of the variable - self.assertFalse( - new_plan_log_records.command_log_ids[1].is_skipped, - msg="The second plan line should not be skipped", - ) - - def test_flight_plan_copy(self): - """Test duplicating a Flight Plan with lines, actions, and variable values""" - - # Create a Flight Plan - plan = self.Plan.create( - { - "name": "Test Flight Plan", - "note": "Test Note", - } - ) - - # Create a command for the plan line - command = self.Command.create( - { - "name": "Test Command", - # Command to get Linux kernel version - "code": "uname -r", - } - ) - - # Create a Flight Plan Line - plan_line = self.plan_line.create( - { - "plan_id": plan.id, - "command_id": command.id, - "path": "/test/path", - # Condition based on Linux version - "condition": '{{ test_linux_version }} >= "5.0"', - } - ) - - # Create a variable for the action - variable = self.Variable.create({"name": "test_linux_version"}) - - # Create an Action for the Plan Line - action = self.plan_line_action.create( - { - "line_id": plan_line.id, - "action": "n", # next action - "condition": "==", - "value_char": "0", # condition for success - } - ) - - # Create a Variable Value for the Action - self.env["cx.tower.variable.value"].create( - { - "variable_id": variable.id, - "value_char": "5.0", - "plan_line_action_id": action.id, - } - ) - - # Duplicate the Flight Plan - copied_plan = plan.copy() - - # Ensure the new Flight Plan was created with a new ID - self.assertNotEqual( - copied_plan.id, - plan.id, - "Copied plan should have a different ID from the original", - ) - - # Check that the copied plan has the same number of lines - self.assertEqual( - len(copied_plan.line_ids), - len(plan.line_ids), - "Copied plan should have the same number of lines as the original", - ) - - # Check that the copied plan's lines have the same actions as the original - original_line = plan.line_ids - copied_line = copied_plan.line_ids - - # Ensure the command, condition, and custom path are copied correctly - self.assertEqual( - copied_line.command_id.id, - original_line.command_id.id, - "Command should be the same in copied line", - ) - self.assertEqual( - copied_line.path, - original_line.path, - "Custom path should be the same in copied line", - ) - self.assertEqual( - copied_line.condition, - original_line.condition, - "Condition should be the same in copied line", - ) - - # Ensure actions were copied correctly - self.assertEqual( - len(copied_line.action_ids), - len(original_line.action_ids), - "Number of actions should be the same in the copied line", - ) - self.assertEqual( - copied_line.action_ids.action, - original_line.action_ids.action, - "Action should be the same in the copied line", - ) - self.assertEqual( - copied_line.action_ids.condition, - original_line.action_ids.condition, - "Action condition should be the same in the copied line", - ) - self.assertEqual( - copied_line.action_ids.value_char, - original_line.action_ids.value_char, - "Action value should be the same in the copied line", - ) - - # Check that variable values were copied correctly - original_action = original_line.action_ids - copied_action = copied_line.action_ids - - self.assertEqual( - len(copied_action.variable_value_ids), - len(original_action.variable_value_ids), - "Number of variable values should be the same in the copied action", - ) - - self.assertEqual( - copied_action.variable_value_ids.variable_id.id, - original_action.variable_value_ids.variable_id.id, - "Variable should be the same in the copied action", - ) - self.assertEqual( - copied_action.variable_value_ids.value_char, - original_action.variable_value_ids.value_char, - "Variable value should be the same in the copied action", - ) - - def test_plan_with_another_plan(self): - """ - Test to check running another plan from current plan - """ - # Check plan logs - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 0, "Plan logs should be empty") - # Run plan - self.plan_2._run_single(self.server_test_1) - # Check plan logs after execute command with plan action - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 2, msg="Should be 2 plan logs") - - parent_plan_log = plan_log_records.filtered( - lambda rec: rec.plan_id == self.plan_2 - ) - self.assertTrue(parent_plan_log, "The log for Plan 2 must exist!") - self.assertEqual( - parent_plan_log.plan_status, 0, "Plan log should success status" - ) - - child_plan_log = plan_log_records - parent_plan_log - self.assertEqual( - child_plan_log.parent_flight_plan_log_id, - parent_plan_log, - "Second plan log should contain parent log link", - ) - self.assertEqual( - child_plan_log.plan_status, - parent_plan_log.command_log_ids.command_status, - "The command status of main plan should be equal " - "of status second flight plan", - ) - self.assertEqual( - parent_plan_log.command_log_ids.triggered_plan_log_id, - child_plan_log, - "The command triggered plan line should be equal to child plan", - ) - - # Check that we cannot add recursive plan - with self.assertRaisesRegex( - ValidationError, "Recursive plan call detected in plan.*" - ): - self.plan_line.create( - { - "sequence": 20, - "plan_id": self.plan_1.id, - "command_id": self.command_run_flight_plan_1.id, - } - ) - - # Delete plan lines from first plan - self.plan_1.line_ids = False - # Run plan - self.plan_2._run_single(self.server_test_1) - plan_log_records = ( - self.PlanLog.search([("server_id", "=", self.server_test_1.id)]) - - plan_log_records - ) - - parent_plan_log = plan_log_records.filtered( - lambda rec: rec.plan_id == self.plan_2 - ) - self.assertTrue(parent_plan_log, "The log for Plan 2 must exist!") - self.assertEqual( - parent_plan_log.plan_status, PLAN_IS_EMPTY, "Plan log should failed status" - ) - - child_plan_log = plan_log_records - parent_plan_log - self.assertEqual( - child_plan_log.parent_flight_plan_log_id, - parent_plan_log, - "Second plan log should contain parent log link", - ) - self.assertEqual( - child_plan_log.plan_status, - parent_plan_log.command_log_ids.command_status, - "The command status of parent plan should be equal " - "of status second flight plan", - ) - - def test_plan_with_two_plans(self): - """ - Test to check two plans from plan - """ - self.plan_line.create( - { - "sequence": 15, - "plan_id": self.plan_2.id, - "command_id": self.command_run_flight_plan_1.id, - } - ) - # Check plan logs - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 0, "Plan logs should be empty") - # Run plan - self.plan_2._run_single(self.server_test_1) - # Check plan logs after execute command with plan action - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 3, msg="Should be 3 plan logs") - - def test_plan_with_nested_plans(self): - """ - Test to check two plans from plan - """ - command_run_flight_plan_2 = self.Command.create( - { - "name": "Run Flight Plan", - "action": "plan", - "flight_plan_id": self.plan_2.id, - } - ) - plan_3 = self.Plan.create( - { - "name": "Test plan 3", - "note": "Run flight plan 2", - } - ) - self.plan_line.create( - { - "sequence": 5, - "plan_id": plan_3.id, - "command_id": command_run_flight_plan_2.id, - } - ) - # Check plan logs - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 0, "Plan logs should be empty") - # Run plan - plan_3._run_single(self.server_test_1) - # Check plan logs after execute command with plan action - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 3, msg="Should be 3 plan logs") - - last_child_plan_log = plan_log_records.filtered( - lambda rec: rec.plan_id == self.plan_1 - ) - self.assertTrue(last_child_plan_log, "The log for Plan 1 must exist!") - self.assertEqual( - last_child_plan_log.plan_status, 0, "Plan log should success status" - ) - - self.assertIn( - last_child_plan_log.parent_flight_plan_log_id, - plan_log_records, - "Parent plan logs should exist", - ) - self.assertEqual( - last_child_plan_log.parent_flight_plan_log_id.plan_id, - self.plan_2, - "Parent plan should be equal to plan 2", - ) - - child_plan_log = plan_log_records.filtered( - lambda rec: rec.plan_id == self.plan_2 - ) - self.assertIn( - child_plan_log.parent_flight_plan_log_id, - plan_log_records, - "Parent plan logs should exist", - ) - self.assertEqual( - child_plan_log.parent_flight_plan_log_id.plan_id, - plan_3, - "Parent plan should be equal to plan 3", - ) - self.assertEqual( - child_plan_log.command_log_ids.triggered_plan_log_id, - last_child_plan_log, - "The command triggered plan line should be equal to last child plan", - ) - self.assertEqual( - child_plan_log.command_log_ids.triggered_plan_log_id, - last_child_plan_log, - "The command triggered plan line should be equal to last child plan", - ) - parent_plan_log = plan_log_records - child_plan_log - last_child_plan_log - self.assertEqual( - parent_plan_log.command_log_ids.triggered_plan_log_id, - child_plan_log, - "The command triggered plan line from parent plan " - "should be equal to child plan", - ) - - # Check that we cannot change command with existing plan, - # because it's recursive plan - with self.assertRaisesRegex( - ValidationError, "Recursive plan call detected in plan.*" - ): - self.plan_line_1.write( - { - "command_id": command_run_flight_plan_2.id, - } - ) - - # Set the previous command back - - self.plan_line_1.write( - { - "command_id": self.command_create_dir.id, - } - ) - # --- Check server dependency handling - - # Remove all existing flight plan logs - self.PlanLog.search([]).unlink() - - # Set server dependency for plan 2 - self.plan_2.write( - { - "server_ids": [(6, 0, [self.server.id])], - } - ) - plan_log = self.server_test_1.run_flight_plan(self.plan_2) - self.assertEqual(plan_log.plan_status, PLAN_NOT_COMPATIBLE_WITH_SERVER) - - # Run plan on allowed server - plan_log = self.server.run_flight_plan(self.plan_2) - self.assertEqual(plan_log.plan_status, 0) - - def test_failed_first_child_plan_with_another_plan(self): - """ - Check that child plan was failed then parent plan is failed too - """ - # Add new plan line - self.plan_line.create( - { - "sequence": 15, - "plan_id": self.plan_2.id, - "command_id": self.command_run_flight_plan_1.id, - } - ) - # Check plan logs - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 0, "Plan logs should be empty") - - # Simulate a failed Plan 1. To achieve this, we need to update the command - # associated with Plan 1 to apply the desired side effect. - self.plan_1.line_ids.command_id[0].code = "fail" - - # Run plan - self.plan_2._run_single(self.server_test_1) - - # Check plan logs after execute command with plan action - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - # 2 logs only because plan should exist with error after first failed command - self.assertEqual(len(plan_log_records), 2, msg="Should be 2 plan logs") - - parent_plan_log = plan_log_records.filtered( - lambda rec: rec.plan_id == self.plan_2 - ) - self.assertTrue(parent_plan_log, "The log for Plan 2 must exist!") - self.assertEqual( - parent_plan_log.plan_status, GENERAL_ERROR, "Plan log should failed status" - ) - - child_plan_log = plan_log_records - parent_plan_log - self.assertEqual( - child_plan_log.parent_flight_plan_log_id, - parent_plan_log, - "Second plan log should contain parent log link", - ) - self.assertEqual( - child_plan_log.plan_status, - parent_plan_log.command_log_ids.command_status, - "The command status of main plan should be equal " - "of status second flight plan", - ) - - def test_failed_second_child_plan_with_another_plan(self): - """ - Check that child plan was failed then parent plan is failed too - """ - # Add new plan line - line = self.plan_line.create( - { - "sequence": 15, - "plan_id": self.plan_2.id, - "command_id": self.command_run_flight_plan_1.id, - } - ) - - cx_tower_plan_obj = self.registry["cx.tower.plan"] - _run_single_super = cx_tower_plan_obj._run_single - - def _run_single(this, *args, **kwargs): - if ( - this == self.plan_1 - and this.env["cx.tower.plan.log"] - .browse(kwargs["log"]["plan_log_id"]) - .plan_line_executed_id - == line - ): - # Simulate a failed Plan 1. To achieve this, we need to update - # the command associated with Plan 1 to apply the desired side effect. - self.plan_1.line_ids.command_id[0].code = "fail" - return _run_single_super(this, *args, **kwargs) - - with patch.object(cx_tower_plan_obj, "_run_single", _run_single): - # Run plan - self.plan_2._run_single(self.server_test_1) - - # Check plan logs after execute command with plan action - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - # 3 logs because plan should exist with error after second failed command - self.assertEqual(len(plan_log_records), 3, msg="Should be 3 plan logs") - - parent_plan_log = plan_log_records.filtered( - lambda rec: rec.plan_id == self.plan_2 - ) - self.assertTrue(parent_plan_log, "The log for Plan 2 must exist!") - self.assertEqual( - parent_plan_log.plan_status, GENERAL_ERROR, "Plan log should failed status" - ) - - child_plan_log = plan_log_records - parent_plan_log - self.assertEqual( - child_plan_log.parent_flight_plan_log_id, - parent_plan_log, - "Second plan log should contain parent log link", - ) - self.assertEqual( - len(child_plan_log), - 2, - "Must be 2 child plan logs", - ) - self.assertIn( - GENERAL_ERROR, - child_plan_log.mapped("plan_status"), - "One of plan status of child plan must be GENERAL_ERROR", - ) - self.assertIn( - 0, - child_plan_log.mapped("plan_status"), - "One of plan status of child plan must be GENERAL_ERROR", - ) - - def test_plan_with_another_plan_with_condition(self): - """ - Test that parent plan will success finished - if child plan executable by condition - """ - # Add condition for first plan line - self.plan_line_1.condition = "1 == 1" - # Check plan logs - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 0, "Plan logs should be empty") - # Run plan - self.plan_2._run_single(self.server_test_1) - # Check plan logs after execute command with plan action - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - - self.assertEqual(len(plan_log_records), 2, msg="Should be 2 plan logs") - - parent_plan_log = plan_log_records.filtered( - lambda rec: rec.plan_id == self.plan_2 - ) - self.assertTrue(parent_plan_log, "The log for Plan 2 must exist!") - self.assertEqual( - parent_plan_log.plan_status, 0, "Plan log should success status" - ) - - child_plan_log = plan_log_records - parent_plan_log - self.assertEqual( - child_plan_log.parent_flight_plan_log_id, - parent_plan_log, - "Second plan log should contain parent log link", - ) - self.assertEqual( - child_plan_log.plan_status, - parent_plan_log.command_log_ids.command_status, - "The command status of main plan should be equal " - "of status second flight plan", - ) - - def test_plan_with_another_plan_with_not_executable_condition(self): - """ - Test plan with not executable condition for second plan line - """ - # Add condition for first plan line - self.plan_line_1.condition = "{{ odoo_version }} == '14.0'" - # Check plan logs - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - self.assertEqual(len(plan_log_records), 0, "Plan logs should be empty") - # Run plan - self.plan_2._run_single(self.server_test_1) - - # Check plan logs after execute command with plan action - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - - self.assertEqual(len(plan_log_records), 2, msg="Should be 2 plan logs") - - self.assertIn( - PLAN_LINE_CONDITION_CHECK_FAILED, - plan_log_records.command_log_ids.mapped("command_status"), - "One of commands should be skipped", - ) - - def test_plan_with_another_plan_with_all_not_executable_condition(self): - """ - Test plan with not executable condition for second plan line - """ - # Add condition for all plan lines - self.plan_line_1.condition = "{{ odoo_version }} == '14.0'" - self.plan_line_2.condition = "{{ odoo_version }} == '14.0'" - - self.plan_2_line_1.condition = "{{ odoo_version }} == '14.0'" - self.plan_2_line_2.condition = "{{ odoo_version }} == '14.0'" - - self.plan_2._run_single(self.server_test_1) - - # Check plan logs after execute command with plan action - plan_log_records = self.PlanLog.search( - [("server_id", "=", self.server_test_1.id)] - ) - - self.assertEqual(len(plan_log_records), 1, msg="Should be 1 plan logs") - self.assertEqual( - PLAN_LINE_CONDITION_CHECK_FAILED, - plan_log_records.command_log_ids.command_status, - "Command status should be skipped", - ) - - def test_plan_unlink(self): - plan = self.plan_1.copy() - plan_id = plan.id - plan_line_ids = plan.line_ids - plan_line_action_ids = plan.mapped("line_ids.action_ids") - - plan.unlink() - - self.assertFalse( - self.Plan.search([("id", "=", plan_id)]), msg="Plan should be deleted" - ) - self.assertFalse( - self.plan_line.search([("id", "in", plan_line_ids.ids)]), - msg="Plan line should be deleted when Plan is deleted", - ) - self.assertFalse( - self.plan_line_action.search([("id", "in", plan_line_action_ids.ids)]), - msg="Plan line action should be deleted when Plan line is deleted", - ) - - def test_plan_command_server_compatibility(self): - """Test plan execution with server-restricted flight plans""" - # Create a new test server - test_server = self.Server.create( - { - "name": "Test Server", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "host_key": "test_key", - } - ) - - # Create a flight plan restricted to the test server - plan = self.Plan.create( - { - "name": "Server Restricted Plan", - "server_ids": [(6, 0, [test_server.id])], - "line_ids": [ - (0, 0, {"command_id": self.command_create_dir.id, "sequence": 1}) - ], - } - ) - - # Should fail when executing on non-allowed server - plan_log = plan._run_single(self.server_test_1) - self.assertEqual(plan_log.plan_status, PLAN_NOT_COMPATIBLE_WITH_SERVER) - - # Should work on allowed server - plan._run_single(test_server) - plan_log = self.PlanLog.search( - [("plan_id", "=", plan.id), ("server_id", "=", test_server.id)], limit=1 - ) - self.assertEqual(plan_log.command_log_ids.command_status, 0) - - def test_another_plan_running(self): - """Test the parallel plan running""" - - # Ensure that the plan doesn't allow parallel running - self.plan_1.write({"allow_parallel_run": False}) - - # Create a new plan log with a plan that is already running - self.PlanLog.create( - { - "plan_id": self.plan_1.id, - "server_id": self.server_test_1.id, - "start_date": fields.Datetime.now(), - } - ) - - # Launch the same plan on the same server - plan_log = self.server_test_1.run_flight_plan(self.plan_1) - self.assertEqual(plan_log.plan_status, ANOTHER_PLAN_RUNNING) - - # Now allow parallel running - self.plan_1.write({"allow_parallel_run": True}) - - # Launch the same plan on the same server - plan_log = self.server_test_1.run_flight_plan(self.plan_1) - self.assertEqual(plan_log.plan_status, 0) - - def test_plan_custom_variables(self): - """Test plan with custom variables""" - command_python_1_id = self.command_python_custom_variable_values_1.id - command_python_2_id = self.command_python_custom_variable_values_2.id - - plan = self._create_plan( - **{ - "name": "Plan with custom variables", - "line_ids": [ - ( - 0, - 0, - { - "command_id": command_python_1_id, - "sequence": 1, - }, - ), - (0, 0, {"command_id": self.command_create_dir.id, "sequence": 2}), - ( - 0, - 0, - { - "command_id": command_python_2_id, - "sequence": 3, - }, - ), - (0, 0, {"command_id": self.command_create_dir.id, "sequence": 4}), - ], - } - ) - - # Run plan - plan_log = self.server_test_1.run_flight_plan(plan) - - # Check that custom variable values were updated correctly - # (The log of plan should contain the last updatedvalues) - self.assertEqual(plan_log.variable_values["test_path_"], "/another_test_path") - self.assertEqual(plan_log.variable_values["test_dir"], "test_dir") - self.assertEqual( - plan_log.variable_values["random_var_reference"], "random_var_value" - ) - self.assertEqual(plan_log.variable_values["_my_value"], "Just To Test") - - command_logs = plan_log.command_log_ids - self.assertEqual( - len(command_logs), - len(plan.line_ids), - f"Should be {len(plan.line_ids)} command logs.", - ) - - # Check that custom variable values were created correctly - # in first python command log - command_python_command_1_log = command_logs.filtered( - lambda log: log.command_id.id == command_python_1_id - ) - self.assertEqual( - command_python_command_1_log.variable_values["test_path_"], "/test_path" - ) - self.assertEqual( - command_python_command_1_log.variable_values["test_dir"], "test_dir" - ) - self.assertEqual( - command_python_command_1_log.variable_values["_my_value"], "Just To Test" - ) - - # Check that custom variable values used in rendered command code - command_create_dir_logs = command_logs.filtered( - lambda log: log.command_id == self.command_create_dir - ) - first_command_create_dir_log = command_create_dir_logs[0] - second_command_create_dir_log = command_create_dir_logs[1] - - # the first_command_create_dir_log.code is equal to - # 'cd /test_path && mkdir test_dir' - # because rendered code contains custom variable values updated - # from first python command - self.assertEqual( - first_command_create_dir_log.code, "cd /test_path && mkdir test_dir" - ) - - # Check that custom variable values were updated correctly in command logs - command_python_command_2_log = command_logs.filtered( - lambda log: log.command_id.id == command_python_2_id - ) - self.assertEqual( - command_python_command_2_log.variable_values["test_path_"], - "/another_test_path", - ) - self.assertEqual( - command_python_command_2_log.variable_values["test_dir"], "test_dir" - ) - self.assertEqual( - command_python_command_2_log.variable_values["random_var_reference"], - "random_var_value", - ) - self.assertEqual( - command_python_command_2_log.variable_values["_my_value"], "Just To Test" - ) - self.assertEqual( - command_python_command_2_log.variable_values[self.variable_url.reference], - "https://www.cetmix.com", - ) - - # the second_command_create_dir_log.code is equal to - # 'cd /another_test_path && mkdir test_dir' - # because rendered code contains custom variable values updated - # from second python command - self.assertEqual( - second_command_create_dir_log.code, - "cd /another_test_path && mkdir test_dir", - ) - - def test_plan_custom_variables_wizard(self): - """Test plan with custom variables from wizard""" - command_python_1_id = self.command_python_custom_variable_values_1.id - command_python_2_id = self.command_python_custom_variable_values_2.id - plan = self._create_plan( - **{ - "name": "Plan with custom variables", - "line_ids": [ - ( - 0, - 0, - { - "command_id": command_python_1_id, - "sequence": 1, - }, - ), - (0, 0, {"command_id": self.command_create_dir.id, "sequence": 2}), - ( - 0, - 0, - { - "command_id": command_python_2_id, - "sequence": 3, - }, - ), - (0, 0, {"command_id": self.command_create_dir.id, "sequence": 4}), - ], - } - ) - - # Create wizard with custom variable values - wizard = self.env["cx.tower.plan.run.wizard"].create( - { - "plan_id": plan.id, - "server_ids": [(6, 0, [self.server_test_1.id])], - "custom_variable_value_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_version.id, - "value_char": "16.0", - }, - ), - ], - } - ) - - # Run wizard - action = wizard.run_flight_plan() - plan_log = self.PlanLog.search( - [("label", "=", action["context"]["search_default_label"])], - limit=1, - ) - self.assertTrue(plan_log, "Plan log should be created") - - # Check that custom variable values were updated correctly - # (The log of plan should contain the last updated - # values + custom variable value from wizard) - self.assertEqual(plan_log.variable_values["test_path_"], "/another_test_path") - self.assertEqual(plan_log.variable_values["test_dir"], "test_dir") - self.assertEqual( - plan_log.variable_values["random_var_reference"], "random_var_value" - ) - self.assertEqual(plan_log.variable_values["_my_value"], "Just To Test") - self.assertEqual( - plan_log.variable_values[self.variable_version.reference], "16.0" - ) - - def test_plan_with_another_plan_custom_variables(self): - """Test plan with another plan with custom variables""" - # Create plan with next structure: - # Plan 1: - # - Command 1: Run plan 2 - # - Command 2: Run Python command to set custom variable values - # - Command 3: Create directory - # Plan 2: - # - Command 1: Python command to set custom variable values - # - Command 2: Create directory - # - Command 3: Python command to update custom variable values - - command_python_1_id = self.command_python_custom_variable_values_1.id - command_python_2_id = self.command_python_custom_variable_values_2.id - plan2 = self._create_plan( - **{ - "name": "Plan 2", - "line_ids": [ - ( - 0, - 0, - { - "command_id": command_python_1_id, - "sequence": 1, - }, - ), - (0, 0, {"command_id": self.command_create_dir.id, "sequence": 2}), - ( - 0, - 0, - { - "command_id": command_python_2_id, - "sequence": 3, - }, - ), - ], - } - ) - - command_run_plan_2 = self.Command.create( - { - "name": "Run Flight Plan", - "action": "plan", - "flight_plan_id": plan2.id, - } - ) - command_python_custom_variable_values_3 = self.Command.create( - { - "name": "Python command to update custom variable values", - "action": "python_code", - "code": """ -custom_values['random_var_reference'] = 'another_random_var_value' -""", - } - ) - - plan1 = self._create_plan( - **{ - "name": "Plan 1", - "line_ids": [ - (0, 0, {"command_id": command_run_plan_2.id, "sequence": 1}), - ( - 0, - 0, - { - "command_id": command_python_custom_variable_values_3.id, - "sequence": 2, - }, - ), - (0, 0, {"command_id": self.command_create_dir.id, "sequence": 3}), - ], - } - ) - - # Create wizard with custom variable values - wizard = self.env["cx.tower.plan.run.wizard"].create( - { - "plan_id": plan1.id, - "server_ids": [(6, 0, [self.server_test_1.id])], - "custom_variable_value_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_version.id, - "value_char": "16.0", - }, - ), - ( - 0, - 0, - { - "variable_id": self.variable_url.id, - "value_char": "https://www.test.com", - }, - ), - ], - } - ) - - # Run wizard - action = wizard.run_flight_plan() - plan_log = self.PlanLog.search( - [("label", "=", action["context"]["search_default_label"])], - limit=1, - ) - self.assertTrue(plan_log, "Plan log should be created") - - command_logs = plan_log.command_log_ids - self.assertEqual( - len(command_logs), - len(plan1.line_ids), - f"Should be {len(plan1.line_ids)} command logs.", - ) - - # First command log is run plan 2 log that contains custom variable values - # updated from plan 2. This command log should contain the same custom - # variable values as from plan 2 log - run_plan2_command_log = command_logs[0] - run_plan2_command_log_variable_values = run_plan2_command_log.variable_values - - plan2_log = run_plan2_command_log.triggered_plan_log_id - plan2_log_variable_values = plan2_log.variable_values - - # check that variable values are the same - self.assertEqual( - run_plan2_command_log_variable_values, plan2_log_variable_values - ) - - # Before finished command (run child plan): we have next variable values: - # {'test_version': '16.0', 'test_url': 'https://www.test.com'} - - # After finished command (run child plan): we have next variable values: - # { - # 'test_version': '16.0', - # 'test_url': 'https://www.cetmix.com', - # 'test_path_': '/another_test_path', - # 'test_dir': 'test_dir', - # '_my_value': 'Just To Test', - # 'random_var_reference': 'random_var_value' - # } - self.assertEqual( - run_plan2_command_log_variable_values["test_path_"], "/another_test_path" - ) - self.assertEqual(run_plan2_command_log_variable_values["test_dir"], "test_dir") - self.assertEqual( - run_plan2_command_log_variable_values["_my_value"], "Just To Test" - ) - self.assertEqual( - run_plan2_command_log_variable_values["random_var_reference"], - "random_var_value", - ) - self.assertEqual( - run_plan2_command_log_variable_values[self.variable_version.reference], - "16.0", - ) - self.assertEqual( - run_plan2_command_log_variable_values[self.variable_url.reference], - "https://www.cetmix.com", - ) - - # After finished main plan: we have next variable values: - # { - # 'test_version': '16.0', - # 'test_url': 'https://www.cetmix.com', - # 'test_path_': '/another_test_path', - # 'test_dir': 'test_dir', - # '_my_value': 'Just To Test', - # 'random_var_reference': 'another_random_var_value' - # } - self.assertEqual(plan_log.variable_values["test_path_"], "/another_test_path") - self.assertEqual(plan_log.variable_values["test_dir"], "test_dir") - self.assertEqual(plan_log.variable_values["_my_value"], "Just To Test") - self.assertEqual( - plan_log.variable_values["random_var_reference"], "another_random_var_value" - ) - self.assertEqual( - plan_log.variable_values[self.variable_version.reference], "16.0" - ) - self.assertEqual( - plan_log.variable_values[self.variable_url.reference], - "https://www.cetmix.com", - ) - - def test_plan_with_custom_values_in_condition(self): - """ - Ensure that plan line conditions see updated custom_values - produced by previous commands. - - 1) python sets test_path_ = '/test_path' - 2) create_dir with condition "{{ test_path_ }} == '/test_path'" -> executes - 3) python updates test_path_ = '/another_test_path' - 4) create_dir with condition "{{ test_path_ }} == '/another_test_path'" - -> executes - Then invert conditions and check both lines are skipped appropriately. - """ - command_python_1_id = self.command_python_custom_variable_values_1.id - command_python_2_id = self.command_python_custom_variable_values_2.id - - plan = self._create_plan( - **{ - "name": "Plan with custom_values in condition", - "line_ids": [ - (0, 0, {"command_id": command_python_1_id, "sequence": 1}), - ( - 0, - 0, - { - "command_id": self.command_create_dir.id, - "sequence": 2, - "condition": "{{ test_path_ }} == '/test_path'", - }, - ), - (0, 0, {"command_id": command_python_2_id, "sequence": 3}), - ( - 0, - 0, - { - "command_id": self.command_create_dir.id, - "sequence": 4, - "condition": "{{ test_path_ }} == '/another_test_path'", - }, - ), - ], - } - ) - - plan_log = self.server_test_1.run_flight_plan(plan) - - logs = plan_log.command_log_ids - self.assertEqual(len(logs), 4, "Should be 4 command logs") - - create_dir_logs = logs.filtered( - lambda line: line.command_id == self.command_create_dir - ) - self.assertEqual(len(create_dir_logs), 2, "Should be 2 create_dir logs") - - self.assertFalse( - create_dir_logs[0].is_skipped, "First create_dir must be executed" - ) - self.assertFalse( - create_dir_logs[1].is_skipped, "Second create_dir must be executed" - ) - - self.assertIn("/test_path", create_dir_logs[0].code) - self.assertIn("/another_test_path", create_dir_logs[1].code) - - def test_plan_stop_mid_execution(self): - """ - Test that plan is correctly marked as stopped and - further commands are not executed. - """ - plan = self._create_plan( - name="Test Plan Stop", - line_ids=[ - (0, 0, {"command_id": self.command_create_dir.id, "sequence": 1}), - (0, 0, {"command_id": self.command_list_dir.id, "sequence": 2}), - ], - ) - server = self.server_test_1 - - cx_tower_plan_line_obj = self.registry["cx.tower.plan.line"] - _run_super = cx_tower_plan_line_obj._run - - # Save plan_log for control is_running - plan_log_holder = {} - - def fake_run(self, server, plan_log, **kwargs): - # Save plan_log for control is_running - plan_log_holder["log"] = plan_log - - # Call stop() after first command - if len(plan_log.command_log_ids) == 0: - plan_log.stop() - # After this call plan_log should be stopped, - # and finish_date should be filled - # Continue execution in standard way - return _run_super(self, server, plan_log, **kwargs) - - with patch.object(cx_tower_plan_line_obj, "_run", new=fake_run): - plan_log = plan._run_single(server) - - self.assertTrue(plan_log.is_stopped, "Plan should be stopped") - self.assertFalse(plan_log.is_running, "Plan should not be in running status") - self.assertEqual( - plan_log.plan_status, PLAN_STOPPED, "Status should be PLAN_STOPPED" - ) - self.assertTrue(plan_log.finish_date, "Finish date should be filled") - self.assertLessEqual( - len(plan_log.command_log_ids), - 1, - "There should be maximum one command in the log", - ) - - def test_flight_plan_reference_update(self): - """Test flight plan reference update cascades to dependent models""" - # 1. Add a variable value to plan_line_1_action_2 - variable_value = self.VariableValue.create( - { - "variable_id": self.variable_os.id, - "value_char": "Ubuntu 20.04", - "plan_line_action_id": self.plan_line_1_action_2.id, - } - ) - - # Store original references for comparison - original_plan_reference = self.plan_1.reference - original_plan_line_1_reference = self.plan_line_1.reference - original_plan_line_2_reference = self.plan_line_2.reference - original_plan_line_1_action_1_reference = self.plan_line_1_action_1.reference - original_plan_line_1_action_2_reference = self.plan_line_1_action_2.reference - original_plan_line_2_action_1_reference = self.plan_line_2_action_1.reference - original_plan_line_2_action_2_reference = self.plan_line_2_action_2.reference - original_variable_value_reference = variable_value.reference - - # 2. Change the reference for plan_1 to "nice_new_plan" - self.plan_1.write({"reference": "nice_new_plan"}) - - # 3. Verify that references are updated for plan lines - # Invalidate models to refresh all references - self.env["cx.tower.plan"].invalidate_model(["reference"]) - self.env["cx.tower.plan.line"].invalidate_model(["reference"]) - self.env["cx.tower.plan.line.action"].invalidate_model(["reference"]) - self.env["cx.tower.variable.value"].invalidate_model(["reference"]) - - # Check that plan reference was updated - self.assertEqual(self.plan_1.reference, "nice_new_plan") - self.assertNotEqual(self.plan_1.reference, original_plan_reference) - - # Check that plan line references were updated to include the new plan reference - self.assertIn("nice_new_plan", self.plan_line_1.reference) - self.assertIn("nice_new_plan", self.plan_line_2.reference) - self.assertNotEqual(self.plan_line_1.reference, original_plan_line_1_reference) - self.assertNotEqual(self.plan_line_2.reference, original_plan_line_2_reference) - - # Check that plan line action references were updated - self.assertIn("nice_new_plan", self.plan_line_1_action_1.reference) - self.assertIn("nice_new_plan", self.plan_line_1_action_2.reference) - self.assertIn("nice_new_plan", self.plan_line_2_action_1.reference) - self.assertIn("nice_new_plan", self.plan_line_2_action_2.reference) - self.assertNotEqual( - self.plan_line_1_action_1.reference, original_plan_line_1_action_1_reference - ) - self.assertNotEqual( - self.plan_line_1_action_2.reference, original_plan_line_1_action_2_reference - ) - self.assertNotEqual( - self.plan_line_2_action_1.reference, original_plan_line_2_action_1_reference - ) - self.assertNotEqual( - self.plan_line_2_action_2.reference, original_plan_line_2_action_2_reference - ) - - # Check that variable value reference was updated - # to include the new plan reference - self.assertIn("nice_new_plan", variable_value.reference) - self.assertNotEqual(variable_value.reference, original_variable_value_reference) - - # Verify the reference pattern for variable value follows the expected format: - # ___ # noqa: E501 - expected_pattern = ( - f"{self.variable_os.reference}_variable_value_plan_line_action_" - f"{self.plan_line_1_action_2.reference}" - ) - self.assertEqual(variable_value.reference, expected_pattern) - - def test_flight_plan_with_child_plan_command_exception(self): - """ - Test flight plan with child plan where command exception occurs. - - Scenario: - - Main flight plan has 2 commands: - 1. Simple python command (success) - 2. Child flight plan with 2 commands where first fails with command exception - - Verify error propagation: command -> child plan -> main plan - - The command exception is simulated using the existing mocking system - that raises exceptions when commands contain "raise" - """ - - # Create child flight plan with 2 commands - child_plan = self.Plan.create( - { - "name": "Child Plan with Error", - "note": "Child plan that will fail on first command", - } - ) - - # Command 1 of child plan - will fail with command exception - child_command_1 = self.Command.create( - { - "name": "Child Command 1 - Command Exception", - "action": "ssh_command", - "code": "raise", # This will trigger command exception in mock - } - ) - - # Command 2 of child plan - should not execute due to error in command 1 - child_command_2 = self.Command.create( - { - "name": "Child Command 2 - Should Not Run", - "action": "python_code", - "code": """ -result = { - "exit_code": 0, - "message": "This should not execute" -} - """, - } - ) - - # Create plan lines for child plan - self.plan_line.create( - { - "sequence": 10, - "plan_id": child_plan.id, - "command_id": child_command_1.id, - } - ) - self.plan_line.create( - { - "sequence": 20, - "plan_id": child_plan.id, - "command_id": child_command_2.id, - } - ) - - # Create command to run child plan - run_child_plan_command = self.Command.create( - { - "name": "Run Child Plan", - "action": "plan", - "flight_plan_id": child_plan.id, - } - ) - - # Create main flight plan with 2 commands - main_plan = self.Plan.create( - { - "name": "Main Plan with Child Plan", - "note": "Main plan with python command and child plan", - } - ) - - # Command 1 of main plan - simple python command (should succeed) - main_command_1 = self.Command.create( - { - "name": "Main Command 1 - Python Success", - "action": "python_code", - "code": """ -result = { - "exit_code": 0, - "message": "Main plan python command executed successfully" -} - """, - } - ) - - # Command 2 of main plan - run child plan (will fail) - main_command_2 = run_child_plan_command - - # Create plan lines for main plan - self.plan_line.create( - { - "sequence": 10, - "plan_id": main_plan.id, - "command_id": main_command_1.id, - } - ) - self.plan_line.create( - { - "sequence": 20, - "plan_id": main_plan.id, - "command_id": main_command_2.id, - } - ) - # Run the first command again - self.plan_line.create( - { - "sequence": 30, - "plan_id": main_plan.id, - "command_id": main_command_1.id, - } - ) - - # Run the main flight plan - plan_log = self.server_test_1.run_flight_plan(main_plan) - - # Verify main plan finished with error - self.assertNotEqual( - plan_log.plan_status, 0, "Main plan should not finish successfully" - ) - - # Get all plan logs for verification - all_plan_logs = plan_log | self.PlanLog.search( - [("parent_flight_plan_log_id", "=", plan_log.id)] - ) - - # Should have 2 plan logs: main plan and child plan - self.assertEqual( - len(all_plan_logs), 2, "Should have 2 plan logs: main and child" - ) - - main_plan_log = all_plan_logs.filtered(lambda log: log.plan_id == main_plan) - child_plan_log = all_plan_logs.filtered( - lambda log: log.parent_flight_plan_log_id == main_plan_log - ) - - self.assertTrue(main_plan_log, "Main plan log should exist") - self.assertTrue(child_plan_log, "Child plan log should exist") - - # Verify child plan finished with error - # The child plan should finish with an error - # (either SSH_CONNECTION_ERROR or GENERAL_ERROR) - self.assertNotEqual( - child_plan_log.plan_status, - 0, - "Child plan should not finish successfully", - ) - - # Get command logs for verification - all_command_logs = self.CommandLog.search( - [("plan_log_id", "in", all_plan_logs.ids)] - ) - - # Should have 3 command logs: main python, - # run child plan, child command exception - self.assertEqual(len(all_command_logs), 3, "Should have 3 command logs") - - # Find specific command logs - main_python_log = all_command_logs.filtered( - lambda log: log.command_id == main_command_1 - ) - run_child_plan_log = all_command_logs.filtered( - lambda log: log.command_id == main_command_2 - ) - child_ssh_error_log = all_command_logs.filtered( - lambda log: log.command_id == child_command_1 - ) - - # Verify main python command succeeded - self.assertEqual( - main_python_log.command_status, 0, "Main python command should succeed" - ) - self.assertEqual( - main_python_log.command_response, - "Main plan python command executed successfully", - "Main python command should have correct response", - ) - - # Verify run child plan command failed - # The command should fail with an error - # (either SSH_CONNECTION_ERROR or GENERAL_ERROR) - self.assertNotEqual( - run_child_plan_log.command_status, - 0, - "Run child plan command should fail", - ) - - # Verify child SSH command failed - # The SSH command should fail with an error status - # (could be GENERAL_ERROR -100 or 255 depending on how the exception is handled) - self.assertNotEqual( - child_ssh_error_log.command_status, 0, "Child SSH command should fail" - ) - # The error message should contain information about - # the SSH connection failure - # The exact error message may vary depending - # on how the exception is handled - self.assertTrue( - child_ssh_error_log.command_error, - "Child SSH command should have an error message", - ) - - # Verify that child command 2 was not executed (no log for it) - child_command_2_log = all_command_logs.filtered( - lambda log: log.command_id == child_command_2 - ) - self.assertFalse( - child_command_2_log, "Child command 2 should not have been executed" - ) - - # Verify plan log relationships - self.assertEqual( - main_plan_log.command_log_ids, - main_python_log | run_child_plan_log, - "Main plan should have correct command logs", - ) - - self.assertEqual( - child_plan_log.command_log_ids, - child_ssh_error_log, - "Child plan should have only the failed command log", - ) - - # Verify that the error propagated correctly through the hierarchy - # The error should propagate from command -> child plan -> main plan - # The specific error codes may vary depending - # on how the system handles the error - self.assertNotEqual( - main_plan_log.plan_status, - 0, - "Error should propagate from child to main plan", - ) - self.assertNotEqual( - child_plan_log.plan_status, 0, "Error should be present in child plan" - ) - self.assertNotEqual( - child_ssh_error_log.command_status, - 0, - "SSH command should have an error status", - ) - self.assertEqual( - child_ssh_error_log.command_status, - child_plan_log.plan_status, - "Child plan should have the same error status as the SSH command", - ) - self.assertEqual( - child_ssh_error_log.command_status, - main_plan_log.plan_status, - "Main plan should have the same error status as the SSH command", - ) - - def test_skip_command_error_flow(self): - """Plan flow: - 1) success, 2) success, 3) error -> sets command_error variable, - 4) skipped if not var, 5) runs if var and exits -1. - """ - # Create commands - command_success = self.Command.create( - { - "name": "Command -> Success", - "action": "python_code", - "code": "# Just return default values", - } - ) - command_error = self.Command.create( - { - "name": "Command -> Error", - "action": "python_code", - "code": "result = {'exit_code': -100, 'message': 'Error'}", - } - ) - command_after_failed = self.Command.create( - { - "name": "Command -> After failed", - "action": "python_code", - "code": ( - "name = server.name + ' --after-failed-- '\n" - "server.write({'name': name})" - ), - } - ) - command_last_one = self.Command.create( - { - "name": "Command -> The last one", - "action": "python_code", - "code": ( - "name = server.name + ' --last-one-- '\n" - "server.write({'name': name})" - ), - } - ) - - # Variable used in conditions - variable_command_error = self.Variable.create( - { - "name": "command_error", - "reference": "test_command_error", - "variable_type": "s", - } - ) - - # Plan and lines - plan = self.Plan.create( - { - "name": "Test skip command error", - "on_error_action": "e", - "custom_exit_code": 0, - } - ) - - self.plan_line.create( - {"sequence": 10, "plan_id": plan.id, "command_id": command_success.id} - ) - self.plan_line.create( - {"sequence": 20, "plan_id": plan.id, "command_id": command_success.id} - ) - - line3 = self.plan_line.create( - {"sequence": 30, "plan_id": plan.id, "command_id": command_error.id} - ) - action3 = self.plan_line_action.create( - { - "line_id": line3.id, - "sequence": 10, - "condition": "!=", - "value_char": "0", - "action": "n", - } - ) - - self.VariableValue.create( - { - "variable_id": variable_command_error.id, - "value_char": "1", - "plan_line_action_id": action3.id, - } - ) - - self.plan_line.create( - { - "sequence": 40, - "plan_id": plan.id, - "command_id": command_after_failed.id, - "condition": "not {{ test_command_error }}", - "variable_ids": [(6, 0, [variable_command_error.id])], - } - ) - - line5 = self.plan_line.create( - { - "sequence": 50, - "plan_id": plan.id, - "command_id": command_last_one.id, - "condition": "{{ test_command_error }}", - "variable_ids": [(6, 0, [variable_command_error.id])], - } - ) - self.plan_line_action.create( - { - "line_id": line5.id, - "sequence": 10, - "condition": "==", - "value_char": "0", - "action": "ec", - "custom_exit_code": -1, - } - ) - - plan_log = self.server_test_1.run_flight_plan(plan) - - self.assertEqual(len(plan_log.command_log_ids), 5) - logs = plan_log.command_log_ids - self.assertTrue( - all( - log.command_status == 0 - for log in logs.filtered(lambda log: log.command_id == command_success) - ) - ) - - error_log = logs.filtered(lambda log: log.command_id == command_error) - self.assertIn(variable_command_error.reference, error_log.variable_values) - self.assertTrue(error_log.command_status == GENERAL_ERROR) - - self.assertTrue( - logs.filtered(lambda log: log.command_id == command_after_failed).mapped( - "command_status" - )[0] - == PLAN_LINE_CONDITION_CHECK_FAILED - ) - self.assertTrue( - logs.filtered(lambda log: log.command_id == command_last_one).mapped( - "command_status" - )[0] - == 0 - ) - - # Final plan status must be custom exit code -1 from line 5 action - self.assertEqual(plan_log.plan_status, -1) - - def test_plan_line_condition_error(self): - """Test plan line condition error - First line is skipped because of condition error - Second line is executed successfully - """ - # Create commands - command_success = self.Command.create( - { - "name": "Command -> Success", - "action": "python_code", - "code": "# Just return default values", - } - ) - - # Plan and lines - plan = self.Plan.create( - { - "name": "Test plan line condition error", - } - ) - - self.plan_line.create( - { - "sequence": 10, - "plan_id": plan.id, - "command_id": command_success.id, - "condition": "=q", - }, - ) - self.plan_line.create( - {"sequence": 20, "plan_id": plan.id, "command_id": command_success.id} - ) - - with mute_logger("odoo.addons.cetmix_tower_server.models.cx_tower_plan_line"): - plan_log = self.server_test_1.run_flight_plan(plan) - - # Must be 2 command logs - self.assertEqual(len(plan_log.command_log_ids), 2) - logs = plan_log.command_log_ids - self.assertTrue(logs[0].is_skipped) - self.assertTrue(logs[1].command_status == 0) diff --git a/addons/cetmix_tower_server/tests/test_plan_line.py b/addons/cetmix_tower_server/tests/test_plan_line.py deleted file mode 100644 index 7025a85..0000000 --- a/addons/cetmix_tower_server/tests/test_plan_line.py +++ /dev/null @@ -1,540 +0,0 @@ -# Copyright (C) 2025 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.exceptions import AccessError - -from .common import TestTowerCommon - - -class TestTowerPlanLine(TestTowerCommon): - """Test the cx.tower.plan.line model access rights.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create a test plan with access level 1 for user tests - cls.test_plan = cls.Plan.create( - { - "name": "Test Access Plan", - "access_level": "1", - "user_ids": [(6, 0, [cls.user.id])], - "manager_ids": [(6, 0, [cls.manager.id])], - } - ) - - # Create a test plan line - cls.test_line = cls.plan_line.create( - { - "plan_id": cls.test_plan.id, - "command_id": cls.command_create_dir.id, - "sequence": 10, - } - ) - - # Create additional servers for testing server-based access - cls.server_2 = cls.Server.create( - { - "name": "Test Server 2", - "ip_v4_address": "localhost", - "ssh_username": "test2", - "ssh_password": "test2", - "ssh_port": 22, - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - } - ) - - cls.server_3 = cls.Server.create( - { - "name": "Test Server 3", - "ip_v4_address": "localhost", - "ssh_username": "test3", - "ssh_password": "test3", - "ssh_port": 22, - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - } - ) - - def test_user_read_access(self): - """Test user read access to plan lines""" - # Case 1: User should be able to read line when: - # - access_level == "1" - # - user is in plan's user_ids OR server's user_ids - recs = self.plan_line.with_user(self.user).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "User should be able to read line when conditions are met", - ) - - # Case 2: User should not be able to read when access_level > "1" - self.test_plan.write( - { - "access_level": "2", - } - ) - recs = self.plan_line.with_user(self.user).search( - [("id", "=", self.test_line.id)] - ) - self.assertNotIn( - self.test_line, - recs, - "User should not be able to read line when access_level > '1'", - ) - - # Case 3: User should be able to read when in server's user_ids - self.test_plan.write( - { - "access_level": "1", - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - self.server_test_1.write( - { - "user_ids": [(6, 0, [self.user.id])], - } - ) - recs = self.plan_line.with_user(self.user).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "User should be able to read line when in server's user_ids", - ) - - def test_user_write_create_unlink_access(self): - """Test user write/create/unlink access restrictions""" - # Users should not be able to create lines - with self.assertRaises(AccessError): - self.plan_line.with_user(self.user).create( - { - "plan_id": self.test_plan.id, - "command_id": self.command_create_dir.id, - "sequence": 20, - } - ) - - # Users should not be able to write lines - with self.assertRaises(AccessError): - self.test_line.with_user(self.user).write({"sequence": 30}) - - # Users should not be able to unlink lines - with self.assertRaises(AccessError): - self.test_line.with_user(self.user).unlink() - - def test_manager_read_access(self): - """Test manager read access to plan lines""" - # Case 1: Manager should be able to read when: - # - access_level <= "2" - # - manager is in plan's manager_ids OR user_ids - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "Manager should be able to read line when conditions are met", - ) - - # Case 2: Manager should not be able to read when access_level > "2" - self.test_plan.write( - { - "access_level": "3", - "manager_ids": [(5, 0, 0)], # Remove all managers - } - ) - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertNotIn( - self.test_line, - recs, - "Manager should not be able to read line when access_level > '2'", - ) - - # Case 2.5: Manager not not be able to read when not in plan managers - self.test_plan.write( - { - "access_level": "2", - "manager_ids": [(5, 0, 0)], # Remove all managers - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - self.server_test_1.write( - { - "user_ids": [(5, 0, 0)], # Remove all users - "manager_ids": [(5, 0, 0)], # Remove all managers - } - ) - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertNotIn( - self.test_line, - recs, - "Manager should not be able to read line when access_level > '2'", - ) - - # Case 3: Manager should be able to read when in server's manager_ids - self.test_plan.write( - { - "access_level": "2", - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - self.server_test_1.write( - { - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "Manager should be able to read line when in server's manager_ids", - ) - - def test_manager_write_create_access(self): - """Test manager write/create access to plan lines""" - # Case 1: Manager should be able to create/write when: - # - access_level <= "2" - # - manager is in plan's manager_ids - try: - # Test create - self.plan_line.with_user(self.manager).create( - { - "plan_id": self.test_plan.id, - "command_id": self.command_create_dir.id, - "sequence": 20, - } - ) - # Test write - self.test_line.with_user(self.manager).write({"sequence": 30}) - except AccessError: - self.fail("Manager should be able to create/write when conditions are met") - - # Case 2: Manager should not be able to create/write when access_level > "2" - self.test_plan.write( - { - "access_level": "3", - } - ) - with self.assertRaises(AccessError): - self.plan_line.with_user(self.manager).create( - { - "plan_id": self.test_plan.id, - "command_id": self.command_create_dir.id, - "sequence": 40, - } - ) - with self.assertRaises(AccessError): - self.test_line.with_user(self.manager).write({"sequence": 50}) - - def test_manager_unlink_access(self): - """Test manager unlink access to plan lines""" - # Create line as manager to test unlink rights - line = self.plan_line.with_user(self.manager).create( - { - "plan_id": self.test_plan.id, - "command_id": self.command_create_dir.id, - "sequence": 20, - } - ) - - # Case 1: Manager should be able to unlink when: - # - access_level <= "2" - # - manager is the creator - # - manager is in plan's manager_ids - try: - line.unlink() - except AccessError: - self.fail("Manager should be able to unlink when conditions are met") - - # Case 2: Manager should not be able to unlink lines created by others - line = self.test_line # Created by admin in setUp - with self.assertRaises(AccessError): - line.with_user(self.manager).unlink() - - def test_root_unrestricted_read_access(self): - """Test root user unrestricted read access""" - # Set most restrictive conditions - self.test_plan.write( - { - "access_level": "3", - "user_ids": [(5, 0, 0)], - "manager_ids": [(5, 0, 0)], - "server_ids": [(6, 0, [self.server_2.id, self.server_3.id])], - } - ) - - # Root should still be able to read - recs = self.plan_line.with_user(self.root).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "Root should be able to read regardless of access restrictions", - ) - - # Root should be able to read all records - all_recs = self.plan_line.with_user(self.root).search([]) - self.assertIn( - self.test_line, - all_recs, - "Root should be able to read all records", - ) - - def test_root_unrestricted_write_access(self): - """Test root user unrestricted write access""" - # Set most restrictive conditions - self.test_plan.write( - { - "access_level": "3", - "user_ids": [(5, 0, 0)], - "manager_ids": [(5, 0, 0)], - "server_ids": [(6, 0, [self.server_2.id, self.server_3.id])], - } - ) - - try: - # Test single field update - self.test_line.with_user(self.root).write({"sequence": 100}) - - # Test multiple field update - self.test_line.with_user(self.root).write( - { - "sequence": 200, - "path": "/test/path", - "use_sudo": True, - } - ) - except AccessError: - self.fail("Root should be able to write regardless of access restrictions") - - def test_root_unrestricted_create_access(self): - """Test root user unrestricted create access""" - # Set most restrictive conditions - self.test_plan.write( - { - "access_level": "3", - "user_ids": [(5, 0, 0)], - "manager_ids": [(5, 0, 0)], - "server_ids": [(6, 0, [self.server_2.id, self.server_3.id])], - } - ) - - try: - # Test create with minimal values - new_line_1 = self.plan_line.with_user(self.root).create( - { - "plan_id": self.test_plan.id, - "command_id": self.command_create_dir.id, - } - ) - - # Test create with all values - new_line_2 = self.plan_line.with_user(self.root).create( - { - "plan_id": self.test_plan.id, - "command_id": self.command_create_dir.id, - "sequence": 300, - "path": "/another/test/path", - "use_sudo": True, - "condition": "{{ test_condition }}", - } - ) - - # Verify created records are readable - recs = self.plan_line.with_user(self.root).search( - [("id", "in", [new_line_1.id, new_line_2.id])] - ) - self.assertEqual( - len(recs), - 2, - "Root should be able to read newly created records", - ) - except AccessError: - self.fail("Root should be able to create regardless of access restrictions") - - def test_root_unrestricted_unlink_access(self): - """Test root user unrestricted unlink access""" - # Set most restrictive conditions - self.test_plan.write( - { - "access_level": "3", - "user_ids": [(5, 0, 0)], - "manager_ids": [(5, 0, 0)], - "server_ids": [(6, 0, [self.server_2.id, self.server_3.id])], - } - ) - - # Create test records - test_lines = self.plan_line.with_user(self.root).create( - [ - { - "plan_id": self.test_plan.id, - "command_id": self.command_create_dir.id, - "sequence": seq, - } - for seq in range(400, 403) - ] - ) - - try: - # Test single record unlink - test_lines[0].with_user(self.root).unlink() - - # Test multiple record unlink - test_lines[1:].with_user(self.root).unlink() - - # Verify records are deleted - recs = self.plan_line.with_user(self.root).search( - [("id", "in", test_lines.ids)] - ) - self.assertEqual( - len(recs), - 0, - "Root should be able to delete records completely", - ) - except AccessError: - self.fail("Root should be able to unlink regardless of access restrictions") - - def test_manager_server_based_read_access(self): - """Test manager read access based on server relationships""" - # Remove direct manager access from plan - self.test_plan.write( - { - "manager_ids": [(5, 0, 0)], # Clear manager_ids - "access_level": "2", - } - ) - - # Case 1: No servers linked - should have access - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "Manager should be able to read when no servers are linked", - ) - - # Case 2: Server linked but manager not in server's users/managers - self.test_plan.write( - { - "server_ids": [(6, 0, [self.server_2.id])], - } - ) - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertNotIn( - self.test_line, - recs, - "Manager should not be able to read when not in server's users/managers", - ) - - # Case 3: Manager in server's user_ids - self.server_2.write( - { - "user_ids": [(6, 0, [self.manager.id])], - } - ) - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "Manager should be able to read when in server's user_ids", - ) - - # Case 4: Manager in server's manager_ids - self.server_2.write( - { - "user_ids": [(5, 0, 0)], - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "Manager should be able to read when in server's manager_ids", - ) - - # Case 5: Multiple servers - access through one server - self.test_plan.write( - { - "server_ids": [(6, 0, [self.server_2.id, self.server_3.id])], - } - ) - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertIn( - self.test_line, - recs, - "Manager should be able to read when in at least one server's manager_ids", - ) - - # Case 6: Multiple servers - no access - self.server_2.write( - { - "manager_ids": [(5, 0, 0)], - } - ) - recs = self.plan_line.with_user(self.manager).search( - [("id", "=", self.test_line.id)] - ) - self.assertNotIn( - self.test_line, - recs, - "Manager should not be able to read when not " - "in any server's users/managers", - ) - - def test_manager_server_based_write_access(self): - """Test manager write access based on server relationships""" - # Remove direct manager access from plan - self.test_plan.write( - { - "manager_ids": [(5, 0, 0)], # Clear manager_ids - "access_level": "2", - "server_ids": [(6, 0, [self.server_2.id])], - } - ) - - # Case 1: No server access - should not be able to write - with self.assertRaises(AccessError): - self.test_line.with_user(self.manager).write({"sequence": 40}) - - # Case 2: Manager in server's manager_ids - still should not be able to write - self.server_2.write( - { - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - with self.assertRaises(AccessError): - self.test_line.with_user(self.manager).write({"sequence": 50}) - - # Case 3: Manager in plan's manager_ids - should be able to write - self.test_plan.write( - { - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - try: - self.test_line.with_user(self.manager).write({"sequence": 60}) - except AccessError: - self.fail("Manager should be able to write when in plan's manager_ids") diff --git a/addons/cetmix_tower_server/tests/test_plan_line_action.py b/addons/cetmix_tower_server/tests/test_plan_line_action.py deleted file mode 100644 index ddd694d..0000000 --- a/addons/cetmix_tower_server/tests/test_plan_line_action.py +++ /dev/null @@ -1,255 +0,0 @@ -# Copyright (C) 2025 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.exceptions import AccessError - -from .common import TestTowerCommon - - -class TestTowerPlanLineAction(TestTowerCommon): - """Test the cx.tower.plan.line.action model access rights.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create a test server - cls.server = cls.Server.create( - { - "name": "Test Server", - "ip_v4_address": "localhost", - "ssh_username": "test", - "ssh_password": "test", - "ssh_port": 22, - "user_ids": [(6, 0, [cls.user.id])], - "manager_ids": [(6, 0, [cls.manager.id])], - } - ) - - # Create a test plan with access level 1 for user tests - cls.test_plan = cls.Plan.create( - { - "name": "Test Access Plan", - "access_level": "1", - "user_ids": [(6, 0, [cls.user.id])], - "manager_ids": [(6, 0, [cls.manager.id])], - } - ) - - # Create a test plan line - cls.test_plan_line = cls.plan_line.create( - { - "plan_id": cls.test_plan.id, - "command_id": cls.command_create_dir.id, - "sequence": 10, - } - ) - - # Create a test action - cls.test_action = cls.plan_line_action.create( - { - "line_id": cls.test_plan_line.id, - "condition": "==", - "value_char": "0", - "action": "n", - } - ) - - def test_user_read_access(self): - """Test user read access to plan line actions""" - # Case 1: User should be able to read action when: - # - access_level == "1" - # - user is in plan's user_ids OR server's user_ids - recs = self.plan_line_action.with_user(self.user).search( - [("id", "=", self.test_action.id)] - ) - self.assertIn( - self.test_action, - recs, - "User should be able to read action when conditions are met", - ) - - # Case 2: User should not be able to read when access_level > "1" - self.test_plan.access_level = "2" - recs = self.plan_line_action.with_user(self.user).search( - [("id", "=", self.test_action.id)] - ) - self.assertNotIn( - self.test_action, - recs, - "User should not be able to read action when access_level > '1'", - ) - - # Case 3: User should not be able to read when not in user_ids - self.test_plan.access_level = "1" - self.test_plan.user_ids = [(5, 0, 0)] # Remove all users - recs = self.plan_line_action.with_user(self.user).search( - [("id", "=", self.test_action.id)] - ) - self.assertNotIn( - self.test_action, - recs, - "User should not be able to read action when not in user_ids", - ) - - # Case 4: User should be able to read when in server's user_ids - self.test_plan.server_ids = [(6, 0, [self.server.id])] - recs = self.plan_line_action.with_user(self.user).search( - [("id", "=", self.test_action.id)] - ) - self.assertIn( - self.test_action, - recs, - "User should be able to read action when in server's user_ids", - ) - - def test_user_write_create_unlink_access(self): - """Test user write/create/unlink access restrictions""" - # Users should not be able to create actions - with self.assertRaises(AccessError): - self.plan_line_action.with_user(self.user).create( - { - "line_id": self.test_plan_line.id, - "condition": "==", - "value_char": "0", - "action": "n", - } - ) - - # Users should not be able to write actions - with self.assertRaises(AccessError): - self.test_action.with_user(self.user).write({"value_char": "1"}) - - # Users should not be able to unlink actions - with self.assertRaises(AccessError): - self.test_action.with_user(self.user).unlink() - - def test_manager_read_access(self): - """Test manager read access to plan line actions""" - # Case 1: Manager should be able to read when: - # - access_level <= "2" - # - manager is in plan's manager_ids - recs = self.plan_line_action.with_user(self.manager).search( - [("id", "=", self.test_action.id)] - ) - self.assertIn( - self.test_action, - recs, - "Manager should be able to read action when conditions are met", - ) - - # Case 2: Manager should not be able to read when access_level > "2" - self.test_plan.access_level = "3" - recs = self.plan_line_action.with_user(self.manager).search( - [("id", "=", self.test_action.id)] - ) - self.assertNotIn( - self.test_action, - recs, - "Manager should not be able to read action when access_level > '2'", - ) - - # Case 3: Manager should be able to read when in server's manager_ids - self.test_plan.access_level = "2" - self.test_plan.manager_ids = [(5, 0, 0)] # Remove all managers - self.test_plan.server_ids = [(6, 0, [self.server.id])] - recs = self.plan_line_action.with_user(self.manager).search( - [("id", "=", self.test_action.id)] - ) - self.assertIn( - self.test_action, - recs, - "Manager should be able to read when in server's manager_ids", - ) - - def test_manager_write_create_access(self): - """Test manager write/create access to plan line actions""" - # Case 1: Manager should be able to create/write when: - # - access_level <= "2" - # - manager is in plan's manager_ids - try: - # Test create - self.plan_line_action.with_user(self.manager).create( - { - "line_id": self.test_plan_line.id, - "condition": "==", - "value_char": "1", - "action": "n", - } - ) - # Test write - self.test_action.with_user(self.manager).write({"value_char": "2"}) - except AccessError: - self.fail("Manager should be able to create/write when conditions are met") - - # Case 2: Manager should not be able to create/write when access_level > "2" - self.test_plan.access_level = "3" - with self.assertRaises(AccessError): - self.plan_line_action.with_user(self.manager).create( - { - "line_id": self.test_plan_line.id, - "condition": "==", - "value_char": "1", - "action": "n", - } - ) - with self.assertRaises(AccessError): - self.test_action.with_user(self.manager).write({"value_char": "3"}) - - def test_manager_unlink_access(self): - """Test manager unlink access to plan line actions""" - # Create action as manager to test unlink rights - action = self.plan_line_action.with_user(self.manager).create( - { - "line_id": self.test_plan_line.id, - "condition": "==", - "value_char": "0", - "action": "n", - } - ) - - # Case 1: Manager should be able to unlink when: - # - access_level <= "2" - # - manager is the creator - # - manager is in plan's manager_ids - try: - action.unlink() - except AccessError: - self.fail("Manager should be able to unlink when conditions are met") - - # Case 2: Manager should not be able to unlink actions created by others - action = self.test_action # Created by admin in setUp - with self.assertRaises(AccessError): - action.with_user(self.manager).unlink() - - def test_root_unrestricted_access(self): - """Test root user unrestricted access""" - # Root should have full access regardless of conditions - try: - # Test read - recs = self.plan_line_action.with_user(self.root).search( - [("id", "=", self.test_action.id)] - ) - self.assertIn( - self.test_action, - recs, - "Root should be able to read action without restrictions", - ) - - # Test create - new_action = self.plan_line_action.with_user(self.root).create( - { - "line_id": self.test_plan_line.id, - "condition": "==", - "value_char": "1", - "action": "n", - } - ) - - # Test write - self.test_action.with_user(self.root).write({"value_char": "2"}) - - # Test unlink - new_action.unlink() - except AccessError: - self.fail("Root user should have unrestricted access") diff --git a/addons/cetmix_tower_server/tests/test_plan_log.py b/addons/cetmix_tower_server/tests/test_plan_log.py deleted file mode 100644 index a80f934..0000000 --- a/addons/cetmix_tower_server/tests/test_plan_log.py +++ /dev/null @@ -1,274 +0,0 @@ -# Copyright (C) 2025 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import fields -from odoo.exceptions import AccessError - -from .common import TestTowerCommon - - -class TestTowerPlanLog(TestTowerCommon): - """Test the cx.tower.plan.log model access rights.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create plans with different access levels - cls.plan_level_1 = cls.Plan.create( - { - "name": "Test Plan L1", - "access_level": "1", - } - ) - - cls.plan_level_2 = cls.Plan.create( - { - "name": "Test Plan L2", - "access_level": "2", - } - ) - - cls.plan_level_3 = cls.Plan.create( - { - "name": "Test Plan L3", - "access_level": "3", - } - ) - - # Create test plan logs with specific users - cls.plan_log_1 = ( - cls.PlanLog.with_user(cls.user) - .sudo() - .create( - { - "server_id": cls.server_test_1.id, - "plan_id": cls.plan_level_1.id, - "start_date": fields.Datetime.now(), - } - ) - ) - - cls.plan_log_2 = ( - cls.PlanLog.with_user(cls.manager) - .sudo() - .create( - { - "server_id": cls.server_test_1.id, - "plan_id": cls.plan_level_1.id, - "start_date": fields.Datetime.now(), - } - ) - ) - - # Create additional server for testing - cls.server_2 = cls.Server.create( - { - "name": "Test Server 2", - "ip_v4_address": "localhost", - "ssh_username": "test2", - "ssh_password": "test2", - "ssh_port": 22, - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - } - ) - - def test_user_read_access(self): - """Test user read access to plan logs""" - # Add user to server's user_ids to isolate creator check - self.server_test_1.write( - { - "user_ids": [(6, 0, [self.user.id])], - } - ) - - # Case 1: User should be able to read when: - # - access_level == "1" - # - created by user - # - user is in server's user_ids - recs = self.PlanLog.with_user(self.user).search( - [("id", "in", [self.plan_log_1.id, self.plan_log_2.id])] - ) - self.assertEqual( - len(recs), - 1, - "User should only be able to read their own logs", - ) - self.assertIn( - self.plan_log_1, - recs, - "User should be able to read own logs when conditions are met", - ) - self.assertNotIn( - self.plan_log_2, - recs, - "User should not be able to read logs created by others", - ) - - # Case 2: User should not be able to read when not in server's user_ids - self.server_test_1.write( - { - "user_ids": [(5, 0, 0)], # Remove all users - } - ) - recs = self.PlanLog.with_user(self.user).search( - [("id", "=", self.plan_log_1.id)] - ) - self.assertNotIn( - self.plan_log_1, - recs, - "User should not be able to read when not in server's user_ids", - ) - - # Case 3: User should not be able to read when access_level > "1" - self.server_test_1.write( - { - "user_ids": [(6, 0, [self.user.id])], - } - ) - high_access_log = ( - self.PlanLog.with_user(self.user) - .sudo() - .create( - { - "server_id": self.server_test_1.id, - "plan_id": self.plan_level_2.id, - "start_date": fields.Datetime.now(), - } - ) - ) - recs = self.PlanLog.with_user(self.user).search( - [("id", "=", high_access_log.id)] - ) - self.assertNotIn( - high_access_log, - recs, - "User should not be able to read logs with access_level > '1'" - " even if created by them", - ) - - def test_manager_read_access(self): - """Test manager read access to plan logs""" - # Case 1: Manager should be able to read when: - # - access_level <= "2" - # - manager is in server's manager_ids - self.server_test_1.write( - { - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - recs = self.PlanLog.with_user(self.manager).search( - [("id", "in", [self.plan_log_1.id, self.plan_log_2.id])] - ) - self.assertEqual( - len(recs), - 2, - "Manager should be able to read all logs when in server's manager_ids", - ) - - # Case 2: Manager should be able to read when in server's user_ids - self.server_test_1.write( - { - "manager_ids": [(5, 0, 0)], # Remove all managers - "user_ids": [(6, 0, [self.manager.id])], - } - ) - recs = self.PlanLog.with_user(self.manager).search( - [("id", "in", [self.plan_log_1.id, self.plan_log_2.id])] - ) - self.assertEqual( - len(recs), - 2, - "Manager should be able to read all logs when in server's user_ids", - ) - - # Case 3: Manager should not be able to read when access_level > "2" - high_access_log = ( - self.PlanLog.with_user(self.manager) - .sudo() - .create( - { - "server_id": self.server_test_1.id, - "plan_id": self.plan_level_3.id, - "start_date": fields.Datetime.now(), - } - ) - ) - recs = self.PlanLog.with_user(self.manager).search( - [("id", "=", high_access_log.id)] - ) - self.assertNotIn( - high_access_log, - recs, - "Manager should not be able to read logs with access_level > '2'", - ) - - # Case 4: Manager should not be able to read when he is not - # in users_ids or manager_ids - self.server_test_1.write( - { - "user_ids": [(5, 0, 0)], - "manager_ids": [(5, 0, 0)], - } - ) - recs = self.PlanLog.with_user(self.manager).search( - [("id", "in", [self.plan_log_1.id, self.plan_log_2.id])] - ) - self.assertNotIn( - self.plan_log_1, - recs, - "Manager should not be able to read logs when he is not" - " in users_ids or manager_ids", - ) - - def test_root_read_only_access(self): - """Root can read all plan logs, but cannot create/modify/delete""" - # Create test logs with sudo() - test_logs = self.PlanLog.sudo().create( - [ - { - "server_id": self.server_2.id, - "plan_id": plan.id, - "start_date": fields.Datetime.now(), - } - for plan in [self.plan_level_1, self.plan_level_2, self.plan_level_3] - ] - ) - - # Root should be able to read all logs regardless of: - # - access_level - # - server relationships - # - who created them - recs = self.PlanLog.with_user(self.root).search([("id", "in", test_logs.ids)]) - self.assertEqual( - len(recs), - 3, - "Root should have unrestricted read access to all logs", - ) - - # Root can't create logs - with self.assertRaises(AccessError): - self.PlanLog.with_user(self.root).create( - { - "server_id": self.server_2.id, - "plan_id": self.plan_level_1.id, - "start_date": fields.Datetime.now(), - } - ) - - # Root cannot modify logs - with self.assertRaises(AccessError): - test_logs.with_user(self.root).write({"start_date": fields.Datetime.now()}) - - # Root cannot delete logs - with self.assertRaises(AccessError): - test_logs.with_user(self.root).unlink() - - # Test read on all records - all_recs = self.PlanLog.with_user(self.root).search([]) - self.assertGreater( - len(all_recs), - 0, - "Root should be able to read all plan logs", - ) diff --git a/addons/cetmix_tower_server/tests/test_reference_mixin.py b/addons/cetmix_tower_server/tests/test_reference_mixin.py deleted file mode 100644 index e2dad2c..0000000 --- a/addons/cetmix_tower_server/tests/test_reference_mixin.py +++ /dev/null @@ -1,310 +0,0 @@ -import re - -from .common import TestTowerCommon - - -class TestTowerReference(TestTowerCommon): - """Test reference generation. - We are using ServerTemplate for that. - """ - - @classmethod - def setUpClass(cls): - super().setUpClass() - - cls.plan_test_mixin = cls.Plan.create( - {"name": "Test Plan reference mixin", "note": "Test Note reference mixin"} - ) - - cls.plan_line_reference_mixin = cls.plan_line.create( - { - "plan_id": cls.plan_test_mixin.id, - "sequence": 1, - "command_id": cls.command_list_dir.id, - } - ) - - def test_reference_generation(self): - """Test reference generation""" - - # --- 1 --- - # Check if auto generated reference matches the pattern - reference_pattern = self.ServerTemplate._get_reference_pattern() - self.assertTrue( - re.match(rf"{reference_pattern}", self.server_template_sample.reference), - "Reference doesn't match template", - ) - - # --- 2 --- - # Create a new server template with custom reference - # and ensure that it's fixed according to the pattern - new_template = self.ServerTemplate.create( - {"name": "Such Much Template", "reference": " Some reference x*((*)) "} - ) - self.assertEqual(new_template.reference, "some_reference_x") - - # --- 3 --- - # Try to create another server template with the same reference and ensure - # that its reference is corrected automatically - yet_another_template = self.ServerTemplate.create( - {"name": "Yet another template", "reference": "some_reference_x"} - ) - self.assertEqual(yet_another_template.reference, "some_reference_x_2") - - # -- 4 --- - # Duplicate the server template and ensure that its name and reference - # are generated properly - yet_another_template_copy = yet_another_template.copy() - self.assertEqual(yet_another_template_copy.name, "Yet another template (copy)") - self.assertEqual( - yet_another_template_copy.reference, "yet_another_template_copy" - ) - - # -- 5 --- - # Update reference and ensure that updated value is correct - yet_another_template_copy.write({"reference": " Some reference x*((*)) "}) - self.assertEqual(yet_another_template_copy.reference, "some_reference_x_3") - - # -- 6 --- - # Update template with a new name and remove reference simultaneously - yet_another_template_copy.write({"name": "Doge so like", "reference": False}) - self.assertEqual(yet_another_template_copy.reference, "doge_so_like") - - # -- 7 --- - # Rename the template and ensure reference is not affected - yet_another_template_copy.write({"name": "Chad"}) - self.assertEqual(yet_another_template_copy.reference, "doge_so_like") - - # -- 8 --- - # Remove the reference and ensure it's regenerated from the name - yet_another_template_copy.write({"reference": False}) - self.assertEqual(yet_another_template_copy.reference, "chad") - - # -- 9 -- - # Update record with the same reference name and ensure it remains the same - yet_another_template_copy.write({"reference": "chad"}) - self.assertEqual(yet_another_template_copy.reference, "chad") - - # -- 10 -- - # Create new template with reference set to False - expected_reference = self.ServerTemplate._generate_or_fix_reference( - "Such Much False Template" - ) - new_template_with_false = self.ServerTemplate.create( - {"name": "Such Much False Template", "reference": False} - ) - self.assertEqual( - new_template_with_false.reference, - expected_reference, - "Reference doesn't match expected one", - ) - - # -- 11 -- - # Create new template with reference and name set to a non valid symbol - # Generic model reference should be used as a reference - expected_reference = self.ServerTemplate._get_model_generic_reference() - new_template_with_non_valid_reference = self.ServerTemplate.create( - {"name": "/", "reference": "/"} - ) - self.assertEqual( - new_template_with_non_valid_reference.reference, - expected_reference, - "Reference doesn't match expected one", - ) - - def test_search_by_reference(self): - """Search record by its reference""" - - # Create a new server template with custom reference - server_template = self.ServerTemplate.create( - {"name": "Such Much Template", "reference": "such_much_template"} - ) - - # Search using correct template reference - search_result = self.ServerTemplate.get_by_reference("such_much_template") - self.assertEqual(server_template, search_result, "Template must be found") - - # Search using malformed (case sensitive) - search_result = self.ServerTemplate.get_by_reference("not_much_template") - self.assertEqual(len(search_result), 0, "Result should be empty") - - def test_prepare_references_valid_input(self): - """ - Ensure references are correctly prepared for valid input. - """ - - vals_list = [{"plan_id": self.plan_test_mixin.id}] - result = self.plan_line._prepare_references( - "cx.tower.plan", "plan_id", vals_list - ) - - # Verify the result contains the expected reference - self.assertIn( - self.plan_test_mixin.id, - result, - "The reference ID should be in the result.", - ) - self.assertEqual( - result[self.plan_test_mixin.id], - self.plan_test_mixin.reference, - "The reference should match the expected value.", - ) - - def test_prepare_references_invalid_model_name(self): - """ - Check that an error is raised for an invalid model name. - """ - - vals_list = [{"plan_id": self.plan_test_mixin.id}] - with self.assertRaises(ValueError) as cm: - self.plan_line._prepare_references("invalid.model", "plan_id", vals_list) - - # Confirm the exception message is as expected - self.assertEqual( - str(cm.exception), - "Model 'invalid.model' does not exist. Please provide a valid model name.", - "The error message should indicate an invalid model name.", - ) - - def test_prepare_references_empty_vals_list(self): - """ - Verify that an empty vals_list returns an empty dictionary. - """ - result = self.plan_line._prepare_references("cx.tower.plan", "plan_id", []) - self.assertEqual( - result, - {}, - "The result should be an empty dictionary when vals_list is empty.", - ) - - def test_populate_references_with_valid_input(self): - """ - Ensure references are populated correctly in the provided values list. - """ - vals_list = [{"plan_id": self.plan_test_mixin.id}] - updated_vals = self.plan_line._pre_populate_references( - "cx.tower.plan", "plan_id", vals_list - ) - - # Check the updated values contain the expected reference format - self.assertEqual( - updated_vals[0]["reference"], - f"{self.plan_test_mixin.reference}_plan_line_1", - "The reference should be correctly populated with the suffix.", - ) - - def test_populate_references_missing_field(self): - """ - Confirm that entries missing the required field are handled properly. - """ - - vals_list_with_missing_field = [{"another_key": 123}] - updated_vals_with_missing = self.plan_line._pre_populate_references( - "cx.tower.plan", "plan_id", vals_list_with_missing_field - ) - self.assertEqual( - updated_vals_with_missing[0]["reference"], - "no_plan_line_1", - "Entries missing the required field should have a default reference.", - ) - - def test_populate_references_duplicate_ids(self): - """ - Ensure that duplicate IDs in the input list are correctly - handled and referenced. - """ - vals_list = [ - {"plan_id": self.plan_test_mixin.id}, - {"plan_id": self.plan_test_mixin.id}, - ] - updated_vals = self.plan_line._pre_populate_references( - "cx.tower.plan", "plan_id", vals_list - ) - - # Verify that each duplicate entry has a unique suffix - self.assertEqual( - updated_vals[0]["reference"], - f"{self.plan_test_mixin.reference}_plan_line_1", - "The first duplicate reference should have the correct suffix.", - ) - self.assertEqual( - updated_vals[1]["reference"], - f"{self.plan_test_mixin.reference}_plan_line_2", - "The second duplicate reference should have the correct suffix.", - ) - - def test_populate_references_empty_vals_list(self): - """ - Check that an empty input list returns an empty result - when populating references. - """ - updated_vals = self.plan_line._pre_populate_references( - "cx.tower.plan", "plan_id", [] - ) - self.assertEqual( - updated_vals, - [], - "The result should be an empty list when vals_list is empty.", - ) - - def test_populate_references_reference_present(self): - """ - Check that reference is preserver when present in vals - """ - - vals_list = [ - {"reference": "my_custom_line_1"}, - {"reference": "my_custom_line_2"}, - ] - updated_vals = self.plan_line._pre_populate_references( - "cx.tower.plan", "plan_id", vals_list - ) - self.assertEqual( - updated_vals[0]["reference"], - "my_custom_line_1", - "Original reference must be preserved", - ) - self.assertEqual( - updated_vals[1]["reference"], - "my_custom_line_2", - "Original reference must be preserved", - ) - - def test_populate_references_mixed_scenarios(self): - """Test mixed scenarios with existing and missing references""" - vals_list = [ - {"reference": "my_custom_line_1"}, - {"plan_id": self.plan_test_mixin.id}, # No reference - {"reference": " "}, # Whitespace reference - {"reference": ""}, # Empty reference - {"reference": "\n_"}, # Some irrelevant symbols - ] - updated_vals = self.plan_line._pre_populate_references( - "cx.tower.plan", "plan_id", vals_list - ) - - self.assertEqual( - updated_vals[0]["reference"], - "my_custom_line_1", - "Original reference must be preserved", - ) - self.assertEqual( - updated_vals[1]["reference"], - f"{self.plan_test_mixin.reference}_plan_line_1", - "Missing reference should be generated", - ) - self.assertEqual( - updated_vals[2]["reference"], - "no_plan_line_1", - "Missing reference should be generated", - ) - self.assertEqual( - updated_vals[3]["reference"], - "no_plan_line_2", - "Missing reference should be generated", - ) - self.assertEqual( - updated_vals[4]["reference"], - "no_plan_line_3", - "Missing reference should be generated", - ) diff --git a/addons/cetmix_tower_server/tests/test_scheduled_task.py b/addons/cetmix_tower_server/tests/test_scheduled_task.py deleted file mode 100644 index 88ba407..0000000 --- a/addons/cetmix_tower_server/tests/test_scheduled_task.py +++ /dev/null @@ -1,288 +0,0 @@ -# Copyright (C) 2025 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields -from odoo.exceptions import AccessError - -from .common import TestTowerCommon - - -class TestCxTowerScheduledTask(TestTowerCommon): - """Test the cx.tower.scheduled.task model.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create an additional server for multi-server command test - cls.server_test_2 = cls.Server.create( - { - "name": "Test 2", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "host_key": "test_key", - "os_id": cls.os_debian_10.id, - } - ) - - # Scheduled task: command (multi-server) - cls.command_scheduled_task = cls.ScheduledTask.create( - { - "name": "Test Command Scheduled Task", - "action": "command", - "command_id": cls.command_list_dir.id, - "interval_number": 1, - "interval_type": "days", - "next_call": fields.Datetime.now(), - "server_ids": [(6, 0, [cls.server_test_1.id, cls.server_test_2.id])], - } - ) - - # Scheduled task: plan (single server) - cls.plan_scheduled_task = cls.ScheduledTask.create( - { - "name": "Test Plan Scheduled Task", - "action": "plan", - "plan_id": cls.plan_1.id, - "interval_number": 1, - "interval_type": "days", - "next_call": fields.Datetime.now(), - "server_ids": [(6, 0, [cls.server_test_1.id])], - } - ) - - # Custom variable for task (option type) - cls.variable_odoo_versions = cls.Variable.create( - { - "name": "odoo_versions", - "variable_type": "o", - } - ) - cls.variable_option_16_0 = cls.VariableOption.create( - { - "name": "16.0", - "value_char": "16.0", - "variable_id": cls.variable_odoo_versions.id, - } - ) - - # Add custom variables to tasks - cls.scheduled_task_cv_os = cls.ScheduledTaskCv.create( - { - "scheduled_task_id": cls.command_scheduled_task.id, - "variable_id": cls.variable_os.id, - "value_char": "Windows 2k", - } - ) - cls.scheduled_task_cv_version = cls.ScheduledTaskCv.create( - { - "scheduled_task_id": cls.command_scheduled_task.id, - "variable_id": cls.variable_odoo_versions.id, - "option_id": cls.variable_option_16_0.id, - } - ) - cls.scheduled_task_cv_version_plan = cls.ScheduledTaskCv.create( - { - "scheduled_task_id": cls.plan_scheduled_task.id, - "variable_id": cls.variable_odoo_versions.id, - "option_id": cls.variable_option_16_0.id, - } - ) - - def _assert_log_records(self, log_model, scheduled_task, expected_count): - """Helper: Assert that log records exist for the task""" - logs = log_model.search([("scheduled_task_id", "=", scheduled_task.id)]) - self.assertTrue(logs, f"{log_model._name} logs should be created after run.") - self.assertEqual( - len(logs), - expected_count, - f"Expected {expected_count} logs for {scheduled_task.display_name}, " - f"got {len(logs)}.", - ) - - def _assert_next_and_last_call_changed( - self, task, last_call_before, next_call_before - ): - """Helper: Assert next_call and last_call changed after run""" - task.invalidate_recordset() - self.assertNotEqual( - task.last_call, last_call_before, "last_call must be changed after run." - ) - self.assertNotEqual( - task.next_call, next_call_before, "next_call must be changed after run." - ) - - def test_reserve_tasks_atomic(self): - """Scheduled Task: reserve_tasks must only lock available""" - tasks = self.command_scheduled_task + self.plan_scheduled_task - reserved = tasks._reserve_tasks() - self.assertEqual( - set(reserved.ids), set(tasks.ids), "Both tasks should be reserved" - ) - # Repeated reservation should return empty (already running) - tasks.invalidate_recordset() - reserved_again = tasks._reserve_tasks() - self.assertFalse( - reserved_again, "Already reserved tasks must not be reserved again" - ) - - def test_run_task_command(self): - """Running a scheduled command task creates logs per server.""" - logs_before = self.CommandLog.search( - [("scheduled_task_id", "=", self.command_scheduled_task.id)] - ) - self.assertFalse(logs_before, "No command logs should exist before run.") - - last_call_before = self.command_scheduled_task.last_call - next_call_before = self.command_scheduled_task.next_call - - self.command_scheduled_task._run() - self._assert_next_and_last_call_changed( - self.command_scheduled_task, last_call_before, next_call_before - ) - self._assert_log_records( - self.CommandLog, - self.command_scheduled_task, - expected_count=len(self.command_scheduled_task.server_ids), - ) - - def test_run_task_plan(self): - """Running a scheduled plan task creates one log per server.""" - logs_before = self.PlanLog.search( - [("scheduled_task_id", "=", self.plan_scheduled_task.id)] - ) - self.assertFalse(logs_before, "No plan logs should exist before run.") - - last_call_before = self.plan_scheduled_task.last_call - next_call_before = self.plan_scheduled_task.next_call - - self.plan_scheduled_task._run() - self._assert_next_and_last_call_changed( - self.plan_scheduled_task, last_call_before, next_call_before - ) - self._assert_log_records( - self.PlanLog, - self.plan_scheduled_task, - expected_count=len(self.plan_scheduled_task.server_ids), - ) - - def test_user_write_create_unlink_access(self): - """User: cannot create, write or unlink scheduled tasks.""" - with self.assertRaises(AccessError): - self.ScheduledTask.with_user(self.user).create( - { - "name": "Test", - "action": "command", - "command_id": self.command_list_dir.id, - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - with self.assertRaises(AccessError): - self.command_scheduled_task.with_user(self.user).write({"sequence": 33}) - with self.assertRaises(AccessError): - self.command_scheduled_task.with_user(self.user).unlink() - - def test_manager_read_access(self): - """Manager: can read scheduled task if in manager_ids or in server's - manager_ids/user_ids.""" - self.command_scheduled_task.manager_ids = [(6, 0, [self.manager.id])] - tasks = self.ScheduledTask.with_user(self.manager).search( - [("id", "=", self.command_scheduled_task.id)] - ) - self.assertIn( - self.command_scheduled_task, - tasks, - "Manager should be able to read their task.", - ) - - # Remove from manager_ids, but add to server manager_ids - self.command_scheduled_task.manager_ids = [(6, 0, [])] - self.server_test_1.manager_ids = [(6, 0, [self.manager.id])] - tasks = self.ScheduledTask.with_user(self.manager).search( - [("id", "=", self.command_scheduled_task.id)] - ) - self.assertIn( - self.command_scheduled_task, - tasks, - "Manager should be able to read task via server manager_ids.", - ) - - # Remove manager from everywhere - self.server_test_1.manager_ids = [(6, 0, [])] - tasks = self.ScheduledTask.with_user(self.manager).search( - [("id", "=", self.command_scheduled_task.id)] - ) - self.assertNotIn( - self.command_scheduled_task, - tasks, - "Manager should NOT be able to read task without relation.", - ) - - def test_manager_write_create_access(self): - """Manager: can create/write if in manager_ids, else denied.""" - # Create as manager - task = self.ScheduledTask.with_user(self.manager).create( - { - "name": "Test", - "action": "command", - "command_id": self.command_list_dir.id, - "manager_ids": [(6, 0, [self.manager.id])], - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - try: - task.with_user(self.manager).write({"sequence": 77}) - except AccessError: - self.fail("Manager should be able to write their own scheduled tasks.") - - # Should fail if not in manager_ids - self.command_scheduled_task.manager_ids = [(6, 0, [])] - with self.assertRaises(AccessError): - self.command_scheduled_task.with_user(self.manager).write({"sequence": 11}) - - def test_manager_unlink_access(self): - """Manager: can unlink only their own tasks (in manager_ids & creator).""" - # Create as manager - task = self.ScheduledTask.with_user(self.manager).create( - { - "name": "Test", - "action": "command", - "command_id": self.command_list_dir.id, - "manager_ids": [(6, 0, [self.manager.id])], - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - try: - task.with_user(self.manager).unlink() - except AccessError: - self.fail("Manager should be able to unlink their own task.") - - # Not creator - with self.assertRaises(AccessError): - self.command_scheduled_task.with_user(self.manager).unlink() - - def test_root_unrestricted_access(self): - """Root: full unrestricted access to all scheduled tasks.""" - # Read - tasks = self.ScheduledTask.with_user(self.root).search( - [("id", "=", self.command_scheduled_task.id)] - ) - self.assertIn( - self.command_scheduled_task, tasks, "Root should be able to read any task." - ) - - # Create - task = self.ScheduledTask.with_user(self.root).create( - { - "name": "Test", - "action": "command", - "command_id": self.command_list_dir.id, - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - try: - task.with_user(self.root).write({"sequence": 123}) - task.with_user(self.root).unlink() - except AccessError: - self.fail("Root should be able to write/unlink any scheduled task.") diff --git a/addons/cetmix_tower_server/tests/test_server.py b/addons/cetmix_tower_server/tests/test_server.py deleted file mode 100644 index d279423..0000000 --- a/addons/cetmix_tower_server/tests/test_server.py +++ /dev/null @@ -1,890 +0,0 @@ -from odoo.exceptions import AccessError, ValidationError - -from ..models.constants import COMMAND_NOT_COMPATIBLE_WITH_SERVER -from .common import TestTowerCommon - - -class TestTowerServer(TestTowerCommon): - @classmethod - def setUpClass(cls): - super().setUpClass() - - cls.os_ubuntu_20_04 = cls.env["cx.tower.os"].create({"name": "Ubuntu 20.04"}) - - # Define model variables to avoid unsubscriptable errors - Key = cls.env["cx.tower.key"] - Server = cls.env["cx.tower.server"] - - secret_1 = Key.create( - { - "name": "Secret 1", - "secret_value": "secret_value_1", - "key_type": "s", - }, - ) - secret_2 = Key.create( - { - "name": "Secret 2", - "secret_value": "secret_value_2", - "key_type": "s", - }, - ) - cls.server_test_2 = Server.create( - { - "name": "Test Server #2", - "color": 2, - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "k", - "host_key": "test_key", - "use_sudo": "p", - "ssh_key_id": cls.key_1.id, - "os_id": cls.os_ubuntu_20_04.id, - "secret_ids": [ - ( - 0, - 0, - { - "key_id": secret_1.id, - "secret_value": "secret_value_1", - }, - ), - ( - 0, - 0, - { - "key_id": secret_2.id, - "secret_value": "secret_value_2", - }, - ), - ], - "tag_ids": [(6, 0, [cls.tag_test_production.id])], - } - ) - - # Files - File = cls.env["cx.tower.file"] - cls.server_test_2_file = File.create( - { - "name": "tower_demo_without_template_{{ branch }}.txt", - "source": "tower", - "server_id": cls.server_test_2.id, - "server_dir": "{{ test_path }}", - "code": "Please, check url: {{ url }}", - } - ) - - # Flight plan to delete the server - Command = cls.env["cx.tower.command"] - Plan = cls.env["cx.tower.plan"] - - # Add a command to delete the server - cls.command_delete_server = Command.create( - { - "name": "Python command for deleting server", - "action": "python_code", - "code": """ -partner = env["res.partner"].create({"name": "Partner 1", "ref": "delete_server"}) -result = { - "exit_code": 0, - "message": partner.name, -} - """, - } - ) - - cls.plan_delete_server = Plan.create( - { - "name": "Delete server", - "line_ids": [ - (0, 0, {"command_id": cls.command_delete_server.id, "sequence": 1}), - ], - } - ) - - # Create two test users that belong only to the "User" group. - cls.user1 = cls.Users.create( - { - "name": "Test User 1", - "login": "test_user1", - "email": "test_user1@example.com", - "groups_id": [(6, 0, [cls.group_user.id])], - } - ) - cls.user2 = cls.Users.create( - { - "name": "Test User 2", - "login": "test_user2", - "email": "test_user2@example.com", - "groups_id": [(6, 0, [cls.group_user.id])], - } - ) - # Create two "Manager" group users. - cls.manager1 = cls.Users.create( - { - "name": "Manager 1", - "login": "manager1", - "email": "manager1@example.com", - "groups_id": [(6, 0, [cls.group_manager.id])], - } - ) - cls.manager2 = cls.Users.create( - { - "name": "Manager 2", - "login": "manager2", - "email": "manager2@example.com", - "groups_id": [(6, 0, [cls.group_manager.id])], - } - ) - - def test_server_copy(self): - """Test server copy""" - - # Let's say we have auto sync enabled on one of the files in server 2 - self.server_test_2_file.auto_sync = True - fields_to_check = [ - "ip_v4_address", - "ip_v6_address", - "ssh_username", - "ssh_password", - "ssh_key_id", - ] - - # Crete a log from file of type 'server' - file_for_log = self.File.create( - { - "source": "server", - "name": "test.log", - "server_dir": "/tmp", - "server_id": self.server_test_2.id, - "code": "Some log record - server", - } - ) - - server_log_server = self.ServerLog.create( - { - "name": "Log from file", - "server_id": self.server_test_2.id, - "log_type": "file", - "file_id": file_for_log.id, - } - ) - # Add variable values to server - self.env["cx.tower.variable.value"].create( - { - "server_id": self.server_test_2.id, - "variable_id": self.variable_dir.id, - "value_char": "test", - } - ) - - # Copy server 2 - server_test_2_copy = self.server_test_2.copy() - - # The name of copy should contain '~ (copy)' suffix - self.assertTrue( - server_test_2_copy.name == self.server_test_2.name + " (copy)", - msg="Server name should contain '~ (copy)' suffix!", - ) - - # Check server logs - # Check that the copied server has the same number of server logs - self.assertEqual( - len(server_test_2_copy.server_log_ids), - len(self.server_test_2.server_log_ids), - ( - "Copied template should have the same " - "number of server logs as the original" - ), - ) - - # Ensure the first server log in the copied server matches the original - copied_log = server_test_2_copy.server_log_ids - self.assertEqual( - copied_log.name, - server_log_server.name, - "Server log name should be the same in the copied server", - ) - self.assertEqual( - copied_log.command_id.id, - server_log_server.command_id.id, - "Command ID should be the same in the copied server log", - ) - self.assertEqual( - copied_log.command_id.code, - server_log_server.command_id.code, - "Command code should be the same in the copied server log", - ) - - # Check fields match list - for field_ in fields_to_check: - self.assertTrue( - getattr(server_test_2_copy, field_) - == getattr(self.server_test_2, field_), - msg=( - f"Field {field_} value on server copy " - "does not match with the source!" - ), - ) - - # Check if auto sync is disabled on the all the files - # in the copied server - self.assertTrue( - all([not file.auto_sync for file in server_test_2_copy.file_ids]), - msg="Auto sync should be disabled on all the files in the copied server!", - ) - - # Check if 'keep_when_deleted' option is enabled on all the files - # in the copied server - self.assertTrue( - all([file.keep_when_deleted for file in server_test_2_copy.file_ids]), - msg=( - "keep_when_deleted option should be enabled on all the files " - "in the copied server!" - ), - ) - - # Check if secret values of keys in the copied server are the same - # as in source server - self.assertTrue( - all( - [ - key_copy.secret_value == key_src.secret_value - for key_src, key_copy in zip( # noqa: B905 we need to run on Python 3.10 - self.server_test_2.secret_ids.sudo(), - server_test_2_copy.secret_ids.sudo(), - ) - ] - ), - msg=( - "Secret values of keys in the copied server " - "should be the same as in source server!" - ), - ) - - # Variable names and values in server copy should be the same - # as in source server - self.assertTrue( - all( - [ - var_copy.variable_reference == var_src.variable_reference - and var_copy.value_char == var_src.value_char - for var_src, var_copy in zip( # noqa: B905 we need to run on Python 3.10 - self.server_test_2.variable_value_ids, - server_test_2_copy.variable_value_ids, - ) - ] - ), - msg=( - "Variable names and values in server copy " - "should be the same as in source server!" - ), - ) - - # Copy copied server - server_test_2_new_copy = server_test_2_copy.copy() - # Variable names and values in server copy should be the same - # as in source server - self.assertTrue( - all( - [ - var_copy.variable_reference == var_src.variable_reference - and var_copy.value_char == var_src.value_char - and var_copy.reference == f"{var_src.reference}_copy" - for var_src, var_copy in zip( # noqa: B905 we need to run on Python 3.10 - server_test_2_copy.variable_value_ids, - server_test_2_new_copy.variable_value_ids, - ) - ] - ), - msg=( - "Variable names and values in server copy " - "should be the same as in source server!" - ), - ) - - def test_server_archive_unarchive(self): - """Test Server archived/unarchived""" - server = self.server_test_1.copy() - self.assertTrue(server, msg="Server must be unarchived") - server.toggle_active() - server.toggle_active() - self.assertTrue(server, msg="Server must be unarchived") - - def test_server_unlink(self): - """ - Test cascading deletion of server and its related records. - """ - secret_1 = self.Key.create( - { - "name": "Secret 1", - "secret_value": "secret_value_1", - "key_type": "s", - }, - ) - # Create a test server - server = self.Server.create( - { - "name": "Test Server #3", - "color": 3, - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "k", - "use_sudo": "p", - "ssh_key_id": self.key_1.id, - "host_key": "test_key", - "os_id": self.os_ubuntu_20_04.id, - "secret_ids": [ - ( - 0, - 0, - { - "key_id": secret_1.id, - "secret_value": "secret_value_1", - }, - ), - ], - } - ) - - # Create related file - file = self.File.create( - {"name": "Test File", "server_id": server.id, "source": "server"} - ) - - # Related secret - secret = server.secret_ids[0] - - variable_meme = self.Variable.create({"name": "meme"}) - - # Create related variable value - variable_value = self.env["cx.tower.variable.value"].create( - { - "variable_id": variable_meme.id, # Replace with valid reference - "value_char": "Test Value", - "server_id": server.id, - } - ) - plan_1 = self.Plan.create( - { - "name": "Test plan", - "note": "Create directory and list its content", - } - ) - # Create a related plan log - plan_log = self.PlanLog.create( - { - "server_id": server.id, - "plan_id": plan_1.id, # Replace with valid reference - } - ) - - # Check that all records are created - self.assertTrue(server, "Server should be created successfully") - self.assertTrue(file, "File should be created successfully") - self.assertTrue(secret, "Secret should be created successfully") - self.assertTrue(variable_value, "Variable Value should be created successfully") - self.assertTrue(plan_log, "Plan Log should be created successfully") - - # Collect IDs for verification post-deletion - file_id = file.id - variable_value_id = variable_value.id - plan_log_id = plan_log.id - - # Delete the server - server.unlink() - - # Verify that the server is deleted - self.assertFalse( - self.Server.search([("id", "=", server.id)]), - msg="Server should be deleted", - ) - # Verify that related records are deleted - self.assertFalse( - self.File.search([("id", "=", file_id)]), - msg="File should be deleted when server is deleted", - ) - # Verify that unrelated records are not affected - self.assertTrue( - self.Plan.search([("id", "=", plan_1.id)]), - msg="Unrelated plan should not be deleted when server is deleted", - ) - self.assertFalse( - self.KeyValue.search([("id", "=", secret.id)]), - msg="Secret should be deleted when server is deleted", - ) - self.assertFalse( - self.VariableValue.search([("id", "=", variable_value_id)]), - msg="Variable Value should be deleted when server is deleted", - ) - self.assertFalse( - self.PlanLog.search([("id", "=", plan_log_id)]), - msg="Plan Log should be deleted when server is deleted", - ) - - def test_server_delete_plan_success(self): - """Test server delete plan""" - - # Set plan to delete the server - self.server_test_2.plan_delete_id = self.plan_delete_server.id - - # Delete the server - self.server_test_2.unlink() - - # Check if the server has been deleted - self.assertFalse( - self.server_test_2.exists(), - msg="Server should be deleted", - ) - - # Check if the partner has been created - self.assertTrue( - self.env["res.partner"].search([("ref", "=", "delete_server")]), - msg="Partner should be created", - ) - - def test_server_delete_plan_error(self): - """Test server delete plan error""" - - # Modify the command to fail - self.command_delete_server.code = """ -result = { - "exit_code": 4, - "message": 'Such much error', -} - """ - # Set plan to delete the server - self.server_test_2.plan_delete_id = self.plan_delete_server.id - - # Delete the server - self.server_test_2.unlink() - - # Check if the server has been deleted - self.assertTrue( - self.server_test_2.exists(), - msg="Server should not be deleted", - ) - - self.assertEqual( - self.server_test_2.status, - "delete_error", - msg="Server status should be delete_error", - ) - - # ------------------------------------------------------------ - # ---- Access - # ------------------------------------------------------------ - def test_user_record_not_visible_without_user_ids(self): - """ - Test that a user in the 'cetmix_tower_server.group_user' group cannot see - a Tower Server record if not added to user_ids. - """ - # Create a Tower Server record without any user_ids. - record = self.Server.create( - { - "name": "User Visibility Test", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "user_ids": [(5, 0, 0)], - } - ) - # As user1, search for the record. Since user1's partner is not subscribed, - # the record should not be returned. - records = self.Server.with_user(self.user1).search([("id", "=", record.id)]) - self.assertFalse( - records, - "User1 should not see the record if not added to user_ids.", - ) - - def test_user_record_visible_after_added_to_user_ids(self): - """ - Test that a user sees a Tower Server record after being added to user_ids. - """ - record = self.Server.create( - { - "name": "User Visibility Test", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "user_ids": [(4, self.user1.id)], - } - ) - # Now, as user1 the record should be visible. - records = self.Server.with_user(self.user1).search([("id", "=", record.id)]) - self.assertTrue( - records, - "User1 should see the record after being added to message_partner_ids.", - ) - - def test_only_added_user_can_see(self): - """ - Test that only the added user can see the Tower Server record. - """ - record = self.Server.create( - { - "name": "User Visibility Test", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "user_ids": [(4, self.user1.id)], - } - ) - # Subscribe only user1's partner. - records_user1 = self.Server.with_user(self.user1).search( - [("id", "=", record.id)] - ) - records_user2 = self.Server.with_user(self.user2).search( - [("id", "=", record.id)] - ) - self.assertTrue( - records_user1, "User1 should see the record after being added to user_ids." - ) - self.assertFalse( - records_user2, - "User2 should not see the record if they are not added to user_ids.", - ) - - def test_manager_read_access_as_follower(self): - """A manager should be able to read a record if his partner is a follower.""" - - # Create a record without any managers in manager_ids. - record = self.Server.create( - { - "name": "Test Server (Follower)", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - # Explicitly clear manager_ids - "manager_ids": [(6, 0, [])], - } - ) - # Subscribe manager1 to the record so that his partner becomes a follower. - record.write({"user_ids": [(4, self.manager1.id)]}) - - # As manager1 (a follower) the record should be visible. - records = self.Server.with_user(self.manager1).search([("id", "=", record.id)]) - self.assertTrue(records, "Manager1 (user) must be able to read the record.") - - # As manager2 (not a follower and not in manager_ids) - # the record should not be visible. - records = self.Server.with_user(self.manager2).search([("id", "=", record.id)]) - self.assertFalse( - records, - "Manager2 (not user_ids and not in manager_ids) must not see the record.", - ) - - def test_manager_read_access_as_manager_ids(self): - """A manager should be able to read a record if he is added to manager_ids.""" - - # Create a record with manager2 added to manager_ids. - record = self.Server.create( - { - "name": "Test Server (Manager)", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(6, 0, [self.manager2.id])], - } - ) - # Without adding to user_ids, manager2 should be able to see the record. - records = self.Server.with_user(self.manager2).search([("id", "=", record.id)]) - self.assertTrue( - records, "Manager2 (in manager_ids) must be able to read the record." - ) - - # Manager1 is not added to user_ids nor in manager_ids - # so should not see the record. - records = self.Server.with_user(self.manager1).search([("id", "=", record.id)]) - self.assertFalse( - records, - "Manager1 (neither user_ids nor in manager_ids) must not see the record.", - ) - - # Add manager1 to user_ids - record.write({"user_ids": [(4, self.manager1.id)]}) - records = self.Server.with_user(self.manager1).search([("id", "=", record.id)]) - self.assertTrue( - records, - "Manager1 (added to user_ids) must be able to see the record.", - ) - - def test_manager_write_access(self): - """A manager should be able to update a record only if he is in manager_ids.""" - - # Create a record with no managers. - record = self.Server.create( - { - "name": "Test Server (Write)", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(6, 0, [])], - } - ) - - # Manager1 (not in manager_ids) tries to update: should raise an AccessError. - with self.assertRaises(AccessError): - record.with_user(self.manager1).write({"name": "Updated Name"}) - - # Update the record to include manager1 in manager_ids. - record.write({"manager_ids": [(4, self.manager1.id)]}) - try: - record.with_user(self.manager1).write({"name": "Updated Name"}) - except AccessError: - self.fail( - "Manager1 must be able to update the " - "record after being added to manager_ids." - ) - - def test_manager_create_access(self): - """ - A manager should be allowed to create a record only if he is added - in the "Managers". - """ - # Manager1 attempts to create a record without including himself in manager_ids. - with self.assertRaises(AccessError): - self.Server.with_user(self.manager1).create( - { - "name": "Test Server (Create Denied)", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(6, 0, [])], - } - ) - - # Manager1 creates a record with himself added to manager_ids. - try: - record = self.Server.with_user(self.manager1).create( - { - "name": "Test Server (Create Allowed)", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(6, 0, [self.manager1.id])], - } - ) - self.assertTrue( - record, - "Manager1 must be able to create the record if he is in manager_ids.", - ) - except AccessError: - self.fail( - "Manager1 should be allowed to create a " - "record when included in manager_ids." - ) - - def test_manager_delete_access(self): - """ - A manager should be allowed to delete a record only if: - - He is in the manager_ids field, and - - He is the creator of the record. - """ - - # -- Scenario 1: Manager1 creates a record with himself in manager_ids. - record = self.Server.with_user(self.manager1).create( - { - "name": "Test Server (Delete Allowed)", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(6, 0, [self.manager1.id])], - } - ) - # Manager1 should be able to delete his own record. - try: - record.with_user(self.manager1).unlink() - except AccessError: - self.fail( - "Manager1 must be able to delete his own record if in manager_ids." - ) - - # -- Scenario 2: Manager2 creates a record (with himself in manager_ids). - record2 = self.Server.with_user(self.manager2).create( - { - "name": "Test Server (Delete Denied - Not Creator)", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(6, 0, [self.manager2.id, self.manager1.id])], - } - ) - # Manager1, should not be able to delete record2. - with self.assertRaises(AccessError): - record2.with_user(self.manager1).unlink() - - # Remove manager2 from manager_ids. - record2.write({"manager_ids": [(6, 0, [])]}) - - # Manager2 should not be able to delete record2 now - # because he is not in manager_ids. - with self.assertRaises(AccessError): - record2.with_user(self.manager2).unlink() - - def test_command_server_compatibility(self): - """Test command compatibility with servers""" - # Create a command restricted to specific servers - command = self.Command.create( - { - "name": "Restricted Command", - "action": "ssh_command", - "code": "echo 'test'", - "server_ids": [(6, 0, [self.server_test_1.id])], - } - ) - - # Should work on allowed server - try: - self.server_test_1.run_command(command) - except Exception as e: - self.fail(f"Command should execute on allowed server but failed: {e}") - - # Should fail on non-allowed server - command_result = self.server_test_2.with_context( - no_command_log=True - ).run_command(command) - self.assertEqual( - command_result["status"], - COMMAND_NOT_COMPATIBLE_WITH_SERVER, - "Command should not execute on non-allowed server", - ) - - # Clear all existing command logs - self.CommandLog.search([]).unlink() - # Same test but with command log - self.server_test_2.run_command(command) - - command_log = self.CommandLog.search([]) - self.assertEqual(len(command_log), 1, "Must be a single log record") - self.assertEqual( - command_log.command_status, - COMMAND_NOT_COMPATIBLE_WITH_SERVER, - "Command should not execute on non-allowed server", - ) - - # Command without server restrictions should work on any server - unrestricted_command = self.Command.create( - { - "name": "Unrestricted Command", - "action": "ssh_command", - "code": "echo 'test'", - } - ) - - try: - self.server_test_1.run_command(unrestricted_command) - self.server_test_2.run_command(unrestricted_command) - except Exception as e: - self.fail( - f"Unrestricted command should execute on any server but failed: {e}" - ) - - def test_server_host_key_validation(self): - """Test server host key validation""" - server = self.Server.create( - { - "name": "Test Server", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "host_key": "test_key", - "skip_host_key": False, - } - ) - # Test with host key - server.test_ssh_connection() - - # Test without host key - server.host_key = None - with self.assertRaises(ValidationError): - server.test_ssh_connection() - - # Test with skip_host_key - server.skip_host_key = True - server.test_ssh_connection() - - def test_server_reference_update(self): - """Test server reference update cascades to dependent models""" - # 1. Add a variable value to server_test_1 - variable_value = self.VariableValue.create( - { - "variable_id": self.variable_os.id, - "value_char": "Ubuntu 20.04", - "server_id": self.server_test_1.id, - } - ) - - # 2. Add a file to server_test_1 - server_file = self.File.create( - { - "name": "test_file.txt", - "server_id": self.server_test_1.id, - "source": "tower", - "code": "Test file content", - } - ) - - # Store original references for comparison - original_server_reference = self.server_test_1.reference - original_variable_value_reference = variable_value.reference - original_file_reference = server_file.reference - - # 3. Change the reference for server_test_1 to "awesome_server" - self.server_test_1.write({"reference": "awesome_server"}) - - # 4. Verify that references are updated for dependent models - # Invalidate models to refresh all references - self.env["cx.tower.server"].invalidate_model(["reference"]) - self.env["cx.tower.variable.value"].invalidate_model(["reference"]) - self.env["cx.tower.file"].invalidate_model(["reference"]) - - # Check that server reference was updated - self.assertEqual(self.server_test_1.reference, "awesome_server") - self.assertNotEqual(self.server_test_1.reference, original_server_reference) - - # Check that variable value reference was updated - # to include the new server reference - self.assertIn("awesome_server", variable_value.reference) - self.assertNotEqual(variable_value.reference, original_variable_value_reference) - - # Check that file reference was updated to include the new server reference - self.assertIn("awesome_server", server_file.reference) - self.assertNotEqual(server_file.reference, original_file_reference) - - # Verify the reference pattern for variable value follows the expected format: - # ___ # noqa: E501 - expected_variable_pattern = ( - f"{self.variable_os.reference}_variable_value_server_" - f"{self.server_test_1.reference}" - ) - self.assertEqual(variable_value.reference, expected_variable_pattern) - - # Verify the reference pattern for file follows the expected format: - # __ - expected_file_pattern = f"{self.server_test_1.reference}_file_1" - self.assertEqual(server_file.reference, expected_file_pattern) diff --git a/addons/cetmix_tower_server/tests/test_server_log.py b/addons/cetmix_tower_server/tests/test_server_log.py deleted file mode 100644 index bb52567..0000000 --- a/addons/cetmix_tower_server/tests/test_server_log.py +++ /dev/null @@ -1,310 +0,0 @@ -# Copyright (C) 2025 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.exceptions import AccessError - -from .common import TestTowerCommon - - -class TestTowerServerLog(TestTowerCommon): - """Test the cx.tower.server.log model access rights.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create test server logs with specific users - cls.server_log_1 = ( - cls.ServerLog.with_user(cls.user) - .sudo() - .create( - { - "name": "Test Log 1", - "server_id": cls.server_test_1.id, - "log_type": "file", - "access_level": "1", - } - ) - ) - - cls.server_log_2 = ( - cls.ServerLog.with_user(cls.manager) - .sudo() - .create( - { - "name": "Test Log 2", - "server_id": cls.server_test_1.id, - "log_type": "file", - "access_level": "1", - } - ) - ) - - # Create additional server for testing - cls.server_2 = cls.Server.create( - { - "name": "Test Server 2", - "ip_v4_address": "localhost", - "ssh_username": "test2", - "ssh_password": "test2", - "ssh_port": 22, - "user_ids": [(6, 0, [])], - "manager_ids": [(6, 0, [])], - } - ) - - def test_user_access(self): - """Test user access to server logs""" - # Add user to server's user_ids - self.server_test_1.write( - { - "user_ids": [(6, 0, [self.user.id])], - } - ) - - # Case 1: User should be able to read when: - # - access_level == "1" - # - user is in server's user_ids - recs = self.ServerLog.with_user(self.user).search( - [("id", "in", [self.server_log_1.id, self.server_log_2.id])] - ) - self.assertEqual( - len(recs), - 2, - "User should be able to read all logs with access_level '1'" - " when in user_ids", - ) - - # Case 2: User should not be able to read when not in server's user_ids - self.server_test_1.write( - { - "user_ids": [(5, 0, 0)], # Remove all users - } - ) - recs = self.ServerLog.with_user(self.user).search( - [("id", "=", self.server_log_1.id)] - ) - self.assertEqual( - len(recs), - 0, - "User should not be able to read when not in server's user_ids", - ) - - # Case 3: User should not be able to read when access_level > "1" - self.server_test_1.write( - { - "user_ids": [(6, 0, [self.user.id])], - } - ) - high_access_log = ( - self.ServerLog.with_user(self.user) - .sudo() - .create( - { - "name": "High Access Log", - "server_id": self.server_test_1.id, - "log_type": "file", - "access_level": "2", - } - ) - ) - - recs = self.ServerLog.with_user(self.user).search( - [("id", "=", high_access_log.id)] - ) - self.assertEqual( - len(recs), - 0, - "User should not be able to read logs with access_level > '1'", - ) - - def test_manager_access(self): - """Test manager access to server logs""" - # Add manager to server's manager_ids - self.server_test_1.write( - { - "manager_ids": [(6, 0, [self.manager.id])], - } - ) - - # Case 1: Manager should be able to read when: - # - access_level <= "2" - # - manager is in server's manager_ids - recs = self.ServerLog.with_user(self.manager).search( - [("id", "in", [self.server_log_1.id, self.server_log_2.id])] - ) - self.assertEqual( - len(recs), - 2, - "Manager should be able to read all logs when in manager_ids", - ) - - # Case 2: Manager should be able to create and write when: - # - access_level <= "2" - # - manager is in server's manager_ids - try: - new_log = self.ServerLog.with_user(self.manager).create( - { - "name": "Manager Test Log", - "server_id": self.server_test_1.id, - "log_type": "file", - "access_level": "2", - } - ) - except AccessError: - self.fail( - "Manager should be able to create logs when in server's manager_ids" - ) - - try: - new_log.write({"name": "Updated Name"}) - except AccessError: - self.fail( - "Manager should be able to write logs when in server's manager_ids" - ) - self.assertEqual(new_log.name, "Updated Name") - - # Case 3: Manager should be able to unlink when: - # - access_level <= "2" - # - created by manager - # - manager is in server's manager_ids - try: - new_log.unlink() - except AccessError: - self.fail( - "Manager should be able to unlink own logs when in server's manager_ids" - ) - - # Case 4: Manager should not be able to unlink logs created by others - with self.assertRaises(AccessError): - self.server_log_1.with_user(self.manager).unlink() - - # Case 5: Manager should not be able to access logs with access_level > "2" - high_access_log = ( - self.ServerLog.with_user(self.manager) - .sudo() - .create( - { - "name": "High Access Log", - "server_id": self.server_test_1.id, - "log_type": "file", - "access_level": "3", - } - ) - ) - - recs = self.ServerLog.with_user(self.manager).search( - [("id", "=", high_access_log.id)] - ) - self.assertEqual( - len(recs), - 0, - "Manager should not be able to read logs with access_level > '2'", - ) - - def test_root_access(self): - """Test root user unrestricted access""" - # Create test logs with various conditions - test_logs = self.ServerLog.with_user(self.root).create( - [ - { - "name": f"Root Test Log {level}", - "server_id": self.server_test_1.id, - "log_type": "file", - "access_level": level, - } - for level in ["1", "2", "3"] - ] - ) - - # Root should be able to read all logs regardless of conditions - recs = self.ServerLog.with_user(self.root).search([("id", "in", test_logs.ids)]) - self.assertEqual( - len(recs), - 3, - "Root should have unrestricted read access to all logs", - ) - - # Root should be able to write all logs - try: - for log in test_logs: - log.write({"name": "Updated by Root"}) - except AccessError: - self.fail("Root should be able to write any logs") - - # Root should be able to unlink all logs - try: - test_logs.unlink() - except AccessError: - self.fail("Root should be able to unlink any logs") - - def test_log_text_access_restrictions(self): - """Test log_text field access controls""" - test_log = self.ServerLog.create( - { - "name": "Access Test Log", - "server_id": self.server_test_1.id, - "log_type": "file", - "access_level": "1", - "log_text": "

    Test content

    ", - } - ) - - # 1. Verify read access for all roles - for user in (self.root, self.manager, self.user): - content = test_log.with_user(user).log_text - self.assertEqual( - content, "

    Test content

    ", f"{user.name} should read log_text" - ) - - # 2. Verify write prohibition for all roles - for user in (self.root, self.manager, self.user): - with self.assertRaises( - AccessError, msg=f"{user.name} shouldn't modify log_text" - ): - test_log.with_user(user).write({"log_text": "

    Modified

    "}) - - def test_log_text_refresh_mechanism(self): - """Test log_text can only be updated via refresh action""" - test_log = self.ServerLog.sudo().create( - { - "name": "Refresh Test Log", - "server_id": self.server_test_1.id, - "log_type": "file", - "access_level": "1", - "log_text": "

    Initial

    ", - } - ) - - # 1. Direct write attempts should fail - with self.assertRaises(AccessError): - test_log.sudo().write({"log_text": "

    Illegal Update

    "}) - - # 2. Verify refresh action updates content - original_content = test_log.log_text - test_log.action_update_log() - - self.assertNotEqual( - test_log.log_text, - original_content, - "action_update_log() should update log_text", - ) - - def test_log_text_copy(self): - """Duplicating a log must NOT keep the log output""" - original = self.ServerLog.create( - { - "name": "Original Log", - "server_id": self.server_test_1.id, - "log_type": "file", - "access_level": "1", - "log_text": "

    Original content

    ", - } - ) - - copied = original.copy() - - # log_text must be cleared because copy=False - self.assertFalse(copied.log_text, "Copied log must not keep log_text") - self.assertNotEqual(copied.id, original.id) - self.assertTrue(bool(copied.name)) diff --git a/addons/cetmix_tower_server/tests/test_server_template.py b/addons/cetmix_tower_server/tests/test_server_template.py deleted file mode 100644 index 2b7f074..0000000 --- a/addons/cetmix_tower_server/tests/test_server_template.py +++ /dev/null @@ -1,1073 +0,0 @@ -from odoo.exceptions import AccessError, ValidationError -from odoo.tests.common import Form - -from .common import TestTowerCommon - - -class TestTowerServerTemplate(TestTowerCommon): - """ - Test the server template model - """ - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create two "Manager" group users - cls.manager1 = cls.Users.create( - { - "name": "Manager 1", - "login": "manager1", - "email": "manager1@example.com", - "groups_id": [(6, 0, [cls.group_manager.id])], - } - ) - cls.manager2 = cls.Users.create( - { - "name": "Manager 2", - "login": "manager2", - "email": "manager2@example.com", - "groups_id": [(6, 0, [cls.group_manager.id])], - } - ) - - def test_create_server_from_template(self): - """ - Create new server from template - """ - self.assertFalse( - self.Server.search( - [("server_template_id", "=", self.server_template_sample.id)] - ), - "The servers shouldn't exist", - ) - # add variable values to server template - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "server_template_id": self.server_template_sample.id, - "value_char": "test", - } - ) - - # add delete flight plan - self.server_template_sample.plan_delete_id = self.plan_1.id - - # add server logs to template - command_for_log = self.Command.create( - {"name": "Get system info", "code": "uname -a"} - ) - - server_template_log = self.ServerLog.create( - { - "name": "Log from server template", - "server_template_id": self.server_template_sample.id, - "log_type": "command", - "command_id": command_for_log.id, - } - ) - - self.assertEqual( - len(self.variable_version.value_ids), - 1, - "The variable must have one value only", - ) - - server_log = self.ServerLog.search([("command_id", "=", command_for_log.id)]) - self.assertEqual(len(server_log), 1, "Server log must be one") - - # create new server from template - new_server = self.ServerTemplate.create_server_from_template( - self.server_template_sample.reference, - "server_from_template", - ipv4="0.0.0.0", - ) - - server = self.Server.search( - [("server_template_id", "=", self.server_template_sample.id)] - ) - self.assertEqual(new_server, server, "Servers must be the same") - self.assertEqual( - new_server.name, - "server_from_template", - "Server name must be server_from_template", - ) - self.assertEqual( - new_server.ip_v4_address, "0.0.0.0", "Server IP must be 0.0.0.0" - ) - self.assertEqual( - new_server.os_id, self.os_debian_10, "Server os must be Debian" - ) - self.assertEqual(new_server.ssh_port, 22, "Server SSH Port must be 22") - self.assertEqual( - new_server.ssh_username, "admin", "Server SSH Username must be 'admin'" - ) - self.assertEqual( - new_server._get_secret_value("ssh_password"), - "password", - "Server SSH Password must be 'password'", - ) - self.assertEqual( - new_server.ssh_auth_mode, "p", "Server SSH Auth Mode must be 'p'" - ) - self.assertEqual( - len(self.variable_version.value_ids), - 2, - "The variable must have two value only", - ) - self.assertEqual( - new_server.plan_delete_id, - self.plan_1, - "Server On Delete Plan must be 'Test plan 1'", - ) - - server_log = self.ServerLog.search([("command_id", "=", command_for_log.id)]) - self.assertEqual(len(server_log), 2, "Server log must be two") - - server_log = server_log.filtered(lambda rec: rec.server_id == new_server) - self.assertNotEqual(server_log, server_template_log) - - def test_create_server_from_template_wizard(self): - """ - Create new server from template from wizard - """ - action = self.server_template_sample.action_create_server() - wizard = ( - self.env["cx.tower.server.template.create.wizard"] # pylint: disable=context-overridden we need a new clean context - .with_context(action["context"]) - .new({}) - ) - self.assertEqual( - self.server_template_sample, - wizard.server_template_id, - "Server Templates must be the same", - ) - - self.assertFalse( - self.Server.search( - [("server_template_id", "=", self.server_template_sample.id)] - ), - "The servers shouldn't exist", - ) - - wizard.update( - { - "name": "test", - "ip_v4_address": "0.0.0.0", - "use_sudo": "n", - "partner_id": self.user_bob.partner_id.id, - "os_id": self.os_debian_10.id, - "tag_ids": [(4, self.tag_test_production.id)], - } - ) - action = wizard.action_confirm() - - server = self.Server.search( - [("server_template_id", "=", self.server_template_sample.id)] - ) - self.assertEqual(action["res_id"], server.id, "Server ids must be the same") - self.assertEqual( - server.partner_id, self.user_bob.partner_id, "Partner must be the same" - ) - self.assertEqual(server.os_id, self.os_debian_10, "OS must be the same") - self.assertEqual( - server.tag_ids, self.tag_test_production, "Tag must be the same" - ) - self.assertEqual(server.use_sudo, "n", "Use sudo must be the same") - self.assertEqual(server.ip_v4_address, "0.0.0.0", "IP must be the same") - self.assertEqual(server.name, "test", "Name must be the same") - - def test_create_server_from_template_action(self): - """ - Create new server from action - """ - name = "server from template" - self.assertFalse( - self.Server.search([("name", "=", name)]), - "Server should not exist", - ) - # add variable values to server template - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "server_template_id": self.server_template_sample.id, - "value_char": "test template version", - } - ) - self.VariableValue.create( - { - "variable_id": self.variable_url.id, - "server_template_id": self.server_template_sample.id, - "value_char": "test template url", - } - ) - # add variable option - variable_url_option = self.VariableOption.create( - { - "name": "localhost", - "value_char": "localhost", - "variable_id": self.variable_url.id, - } - ) - - # create new server with new variable - self.ServerTemplate.create_server_from_template( - self.server_template_sample.reference, - "server from template", - ipv4="localhost", - ssh_username="test", - ssh_password="test", - plan_delete_id=self.plan_1.id, - configuration_variables={ - self.variable_version.reference: "test server version", - "new_variable": "new_value", - }, - configuration_variable_options={ - self.variable_url.reference: variable_url_option.reference, - }, - ) - new_server = self.Server.search([("name", "=", name)]) - - self.assertTrue(new_server, "Server must exist!") - self.assertFalse(new_server.plan_delete_id, "On Delete Plan must be empty!") - - self.assertEqual( - len(new_server.variable_value_ids), 3, "Should be 3 variable values!" - ) - - # check variable values - var_version_value = new_server.variable_value_ids.filtered( - lambda rec: rec.variable_id == self.variable_version - ) - self.assertEqual( - var_version_value.value_char, - "test server version", - "Version variable values should be with new values for " - "server from template", - ) - - var_url_value = new_server.variable_value_ids.filtered( - lambda rec: rec.variable_id == self.variable_url - ) - self.assertEqual( - var_url_value.value_char, - variable_url_option.value_char, - "Url variable values should be same as option value", - ) - - var_new_value = new_server.variable_value_ids.filtered( - lambda rec: rec.variable_id.reference == "new_variable" - ) - self.assertTrue(var_new_value, "New variable should exist on the server") - self.assertEqual( - var_new_value.value_char, - "new_value", - "New variable values should be 'new_values'", - ) - - def test_server_template_copy(self): - """ - Test duplicating a Server Template with variable values and server logs - """ - - # A server template - server_template = self.server_template_sample - - # Add variable values to the server template - original_variable_value = self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "server_template_id": server_template.id, - "value_char": "test", - } - ) - - # Create a command for the server log - command_for_log = self.Command.create( - { - "name": "Get system info", - "code": "uname -a", - } - ) - - # Add server logs to the template - original_log = self.ServerLog.create( - { - "name": "Log from server template", - "server_template_id": server_template.id, - "log_type": "command", - "command_id": command_for_log.id, - } - ) - - # Duplicate the server template - copied_template = server_template.copy() - - # Ensure the new server template was created with a new ID - self.assertNotEqual( - copied_template.id, - server_template.id, - "Copied server template should have a different ID from the original", - ) - - # Check that the copied template has the same number of variable values - self.assertEqual( - len(copied_template.variable_value_ids), - len(server_template.variable_value_ids), - ( - "Copied template should have the same " - "number of variable values as the original" - ), - ) - - # Ensure the variable itself was copied (check variable_id) - copied_variable_value = copied_template.variable_value_ids - self.assertEqual( - copied_variable_value.variable_id.id, - original_variable_value.variable_id.id, - "Variable ID should be the same in the copied template", - ) - self.assertEqual( - copied_variable_value.value_char, - original_variable_value.value_char, - "Variable value should be the same in the copied template", - ) - - # Check that the copied template has the same number of server logs - self.assertEqual( - len(copied_template.server_log_ids), - len(server_template.server_log_ids), - ( - "Copied template should have the same " - "number of server logs as the original" - ), - ) - - # Ensure the first server log in the copied template matches the original - copied_log = copied_template.server_log_ids - self.assertEqual( - copied_log.name, - original_log.name, - "Server log name should be the same in the copied template", - ) - self.assertEqual( - copied_log.command_id.id, - original_log.command_id.id, - "Command ID should be the same in the copied server log", - ) - self.assertEqual( - copied_log.command_id.code, - original_log.command_id.code, - "Command code should be the same in the copied server log", - ) - - def test_required_attribute_in_wizard_field(self): - """ - Test that the 'required' attribute - is correctly applied to the 'value_char' field - in the wizard when the variable is marked as required. - """ - # Create a required variable - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "server_template_id": self.server_template_sample.id, - "value_char": "Test Value", - "required": True, - } - ) - - # Open the wizard - wizard = self.env["cx.tower.server.template.create.wizard"].create( - { - "server_template_id": self.server_template_sample.id, - "name": "Test Server", - "ssh_username": "admin", - } - ) - - # Checking that the 'required' flag is passed to the form context - required_fields = [ - line.required - for line in wizard.line_ids - if line.variable_id == self.variable_version - ] - self.assertTrue( - all(required_fields), - "The 'required' attribute should be correctly " - "applied to the 'value_char' field for required variables.", - ) - - def test_successful_server_creation_with_required_variables(self): - """ - Test that a server is successfully created - when all required variables are filled in the wizard. - """ - # Add manager as user of template - self.server_template_sample.user_ids = self.manager - - # Adding a required variable - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "server_template_id": self.server_template_sample.id, - "value_char": "", - "required": True, - } - ) - - # Open the wizard and fill in the data as manager - wizard = ( - self.env["cx.tower.server.template.create.wizard"] - .with_user(self.manager) - .create( - { - "server_template_id": self.server_template_sample.id, - "name": "Test Server With Required Variables", - "ssh_username": "admin", - "line_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_version.id, - "required": True, - }, - ) - ], - } - ) - ) - - # Fill in the value for the required variable - with Form(wizard) as wizard_form: - with wizard_form.line_ids.edit(0) as line: - line.value_char = "Test Value" - wizard_form.save() - - # Checking the successful creation of the server - action = wizard.action_confirm() - self.assertTrue(action, "Server should be created successfully.") - - # Checking that the server has been created - server = self.Server.search( - [ - ("name", "=", "Test Server With Required Variables"), - ("server_template_id", "=", self.server_template_sample.id), - ] - ) - self.assertTrue(server, "Server should exist.") - self.assertEqual( - server.variable_value_ids.filtered( - lambda v: v.variable_id == self.variable_version - ).value_char, - "Test Value", - "The variable value should be saved correctly.", - ) - - def test_optional_variable_with_empty_value(self): - """ - Test that an optional variable - with an empty value is saved correctly - in the wizard and does not block server creation. - """ - # Adding an optional variable - self.VariableValue.create( - { - "variable_id": self.variable_url.id, - "server_template_id": self.server_template_sample.id, - "value_char": "", - "required": False, - } - ) - - # Open the wizard - wizard = self.env["cx.tower.server.template.create.wizard"].create( - { - "server_template_id": self.server_template_sample.id, - "name": "Server With Optional Variable", - "ssh_username": "admin", - "line_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_url.id, - "value_char": "", - "required": False, - }, - ) - ], - } - ) - - # Checking that the wizard is saved without errors - wizard.action_confirm() - - # Checking that the server has been created - server = self.Server.search( - [ - ("name", "=", "Server With Optional Variable"), - ("server_template_id", "=", self.server_template_sample.id), - ] - ) - self.assertTrue( - server, "Server should be created successfully with optional variables." - ) - - # Checking that an optional variable is saved with an empty value - variable = server.variable_value_ids.filtered( - lambda v: v.variable_id == self.variable_url - ) - self.assertTrue(variable, "Optional variable should be attached to the server.") - self.assertEqual( - variable.value_char, "", "Optional variable should have an empty value." - ) - - def test_wizard_without_variables(self): - """ - Test that the wizard does not display - any variables if the server template has none. - """ - # Removing all variables from the template - self.VariableValue.search( - [("server_template_id", "=", self.server_template_sample.id)] - ).unlink() - - # Open the wizard - wizard = self.env["cx.tower.server.template.create.wizard"].create( - { - "server_template_id": self.server_template_sample.id, - "name": "Server Without Variables", - "ssh_username": "admin", - } - ) - - # Checking that the wizard does not contain variables - self.assertFalse(wizard.line_ids, "Wizard should not display any variables.") - - def test_update_required_variable_value(self): - """ - Test that the value of a required variable - can be updated in the wizard and saved correctly. - """ - # Adding a required variable - self.VariableValue.create( - { - "variable_id": self.variable_version.id, - "server_template_id": self.server_template_sample.id, - "value_char": "Old Value", - "required": True, - } - ) - - # Open the wizard and update the variable value - wizard = self.env["cx.tower.server.template.create.wizard"].create( - { - "server_template_id": self.server_template_sample.id, - "name": "Server With Updated Variable", - "ssh_username": "admin", - "line_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_version.id, - "value_char": "New Value", - "required": True, - }, - ) - ], - } - ) - wizard.action_confirm() - - # Checking that the variable value has been updated - server = self.Server.search([("name", "=", "Server With Updated Variable")]) - variable = server.variable_value_ids.filtered( - lambda v: v.variable_id == self.variable_version - ) - self.assertEqual( - variable.value_char, - "New Value", - "The variable value should be updated correctly.", - ) - - def test_optional_variable_handling(self): - """ - Test that optional variables do not block server creation, - even if their values are empty or missing. - """ - # Adding an optional variable to the template - self.VariableValue.create( - { - "variable_id": self.variable_url.id, - "server_template_id": self.server_template_sample.id, - "value_char": "", - "required": False, - } - ) - - # Specify an optional variable with an empty value - values = self.server_template_sample._prepare_server_values( - configuration_variables={self.variable_url.reference: ""} - ) - - # Checking that the optional variable is processed correctly - variable_data = next( - ( - v - for v in values["variable_value_ids"] - if v[2]["variable_id"] == self.variable_url.id - ), - None, - ) - self.assertIsNotNone( - variable_data, - "The optional variable should be included " - "in the server values even if empty.", - ) - self.assertEqual( - variable_data[2]["value_char"], - "", - "Optional variable should have an empty value.", - ) - - def test_server_creation_with_all_required_variables_removed(self): - """ - Test that server creation fails if all required variables - are removed in the wizard. - - Steps: - 1. Create a server template with required variables. - 2. Open the server creation wizard. - 3. Remove all required variables from the wizard. - 4. Attempt to create the server. - - Expected Result: - - ValidationError is raised with a clear message listing missing variables. - """ - # Create a server template with mandatory variables - template = self.ServerTemplate.create( - { - "name": "Template with required variables", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "variable_value_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_path.id, - "value_char": "/var/log", - "required": True, - }, - ), - ( - 0, - 0, - { - "variable_id": self.variable_dir.id, - "value_char": "logs", - "required": True, - }, - ), - ], - } - ) - - # Simulating the launch of a wizard with the removal of all variables - configuration_variables = {} # All variables removed - - # Checking that the server cannot be created - with self.assertRaises(ValidationError) as cm: - template._create_new_server( - name="Server with missing variables", - configuration_variables=configuration_variables, - ) - - # Checking that the error message contains all removed variables - error_message = str(cm.exception) - self.assertIn("Please resolve the following issues", error_message) - self.assertIn("Missing variables: test_path_, test_dir", error_message) - - def test_partial_required_variables_provided(self): - """ - Test that server creation fails if only some required variables - are provided, and the error message includes both missing and empty variables. - """ - # Create a template with mandatory variables - template = self.ServerTemplate.create( - { - "name": "Template with partial variables", - "variable_value_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_path.id, - "value_char": "/var/log", - "required": False, - }, - ), - ( - 0, - 0, - { - "variable_id": self.variable_dir.id, - "required": True, - }, - ), - ], - } - ) - - # Launch the wizard and specify only some of the required variables - configuration_variables = {"test_path_": "/var/log"} # test_dir skipped - - # Checking that the server is not being created - with self.assertRaises(ValidationError) as cm: - template._create_new_server( - name="Server with partial variables", - configuration_variables=configuration_variables, - ) - - # Checking the error message - error_message = str(cm.exception) - self.assertIn("Missing variables: test_dir", error_message) - self.assertNotIn("test_path_", error_message) # test_path_ provided - - def test_empty_values_for_required_variables(self): - """ - Test that server creation fails if required variables - have empty values, and the error message includes these variables. - """ - # Create a template with mandatory variables - template = self.ServerTemplate.create( - { - "name": "Template with empty values", - "variable_value_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_path.id, - "value_char": "", - "required": True, - }, - ), - ( - 0, - 0, - { - "variable_id": self.variable_dir.id, - "value_char": "", - "required": True, - }, - ), - ], - } - ) - - # Run the wizard with empty values for all variables - configuration_variables = {"test_path_": "", "test_dir": ""} - - # Checking that the server is not being created - with self.assertRaises(ValidationError) as cm: - template._create_new_server( - name="Server with empty variables", - configuration_variables=configuration_variables, - ) - - # Checking the error message - error_message = str(cm.exception) - self.assertIn("Empty values for variables: test_path_, test_dir", error_message) - - def test_with_partial_removed_variables_from_wizard(self): - """ - Test that server creation only with specified - variables from wizard and option - """ - # create new variable option - test_variable = self.Variable.create( - { - "name": "Test Variable", - "variable_type": "s", - } - ) - option = self.VariableOption.create( - { - "name": "test", - "value_char": "test", - "variable_id": test_variable.id, - } - ) - - # template with variables - self.server_template_sample.write( - { - "variable_value_ids": [ - ( - 0, - 0, - { - "variable_id": self.variable_path.id, - "value_char": "/var/log", - "required": False, - }, - ), - ( - 0, - 0, - { - "variable_id": test_variable.id, - "option_id": option.id, - "required": False, - }, - ), - ], - } - ) - - action = self.server_template_sample.action_create_server() - - # Open the wizard and fill in the data - wizard = ( - self.env["cx.tower.server.template.create.wizard"] # pylint: disable=context-overridden we new need a new clean context - .with_context(action["context"]) - .create( - { - "name": "Server from Template", - "ip_v4_address": "localhost", - "server_template_id": self.server_template_sample.id, - } - ) - ) - - with Form(wizard) as wizard_form: - wizard_form.line_ids.remove(0) - wizard_form.save() - - wizard.action_confirm() - - server = self.server_template_sample.server_ids - self.assertEqual( - len(server.variable_value_ids), 1, "Server variable must be 1!" - ) - self.assertEqual( - server.variable_value_ids.value_char, - option.value_char, - "The variable value must be equal to the value from the option", - ) - - def test_manager_access_rights(self): - """ - Test manager access rights for Server Template records: - - Read: user is in user_ids or manager_ids - - Write: user is in manager_ids - """ - record = self.ServerTemplate.create( - { - "name": "Manager Access Test", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "user_ids": [(5, 0, 0)], - "manager_ids": [(5, 0, 0)], - } - ) - - # Case 1: No access rights - records = self.ServerTemplate.with_user(self.manager1).search( - [("id", "=", record.id)] - ) - self.assertEqual( - len(records), - 0, - "Manager should not see the record if not added to user_ids or manager_ids", - ) - - # Case 2: Read access through user_ids - record.write({"user_ids": [(4, self.manager1.id)]}) - records = self.ServerTemplate.with_user(self.manager1).search( - [("id", "=", record.id)] - ) - self.assertEqual( - len(records), - 1, - "Manager should see the record when added to user_ids", - ) - - # Write access should still be forbidden - with self.assertRaises(AccessError): - record.with_user(self.manager1).write({"name": "Updated Name"}) - - # Case 3: Full access through manager_ids - record.write( - { - "user_ids": [(5, 0, 0)], - "manager_ids": [(4, self.manager1.id)], - } - ) - - records = self.ServerTemplate.with_user(self.manager1).search( - [("id", "=", record.id)] - ) - self.assertEqual( - len(records), - 1, - "Manager should see the record when added to manager_ids", - ) - - # Write access should now work - try: - record.with_user(self.manager1).write({"name": "Updated Name"}) - except AccessError: - self.fail("Manager should be able to update the record when in manager_ids") - - def test_manager_create_access(self): - """ - Test that a manager can only create a Server Template record - if they add themselves to manager_ids. - """ - # Try to create without adding to manager_ids - with self.assertRaises(AccessError): - self.ServerTemplate.with_user(self.manager1).create( - { - "name": "Create Access Test - Should Fail", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(5, 0, 0)], - } - ) - - # Create with manager_ids - should succeed - record = self.ServerTemplate.with_user(self.manager1).create( - { - "name": "Create Access Test - Should Succeed", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(4, self.manager1.id)], - } - ) - self.assertEqual( - len(self.ServerTemplate.search([("id", "=", record.id)])), - 1, - "Manager should be able to create record when added to manager_ids", - ) - - def test_manager_delete_access(self): - """ - Test that a manager can only delete a Server Template record if: - - They are in manager_ids - - They created the record - """ - # Scenario 1: Manager1 creates and tries to delete their own record - record = self.ServerTemplate.with_user(self.manager1).create( - { - "name": "Delete Access Test - Own Record", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(4, self.manager1.id)], - } - ) - - try: - record.with_user(self.manager1).unlink() - except AccessError: - self.fail( - "Manager should be able to delete their own record if in manager_ids" - ) - - # Scenario 2: Manager2 creates record, Manager1 tries to delete - record2 = self.ServerTemplate.with_user(self.manager2).create( - { - "name": "Delete Access Test - Other's Record", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(6, 0, [self.manager1.id, self.manager2.id])], - } - ) - - # Manager1 should not be able to delete Manager2's record - with self.assertRaises(AccessError): - record2.with_user(self.manager1).unlink() - - # Remove Manager2 from manager_ids - record2.write({"manager_ids": [(5, 0, 0)]}) - - # Manager2 should not be able to delete their record now - with self.assertRaises(AccessError): - record2.with_user(self.manager2).unlink() - - # Scenario 3: Manager1 creates record but is later removed from manager_ids - record3 = self.ServerTemplate.with_user(self.manager1).create( - { - "name": "Delete Access Test - Removed Manager", - "ssh_port": 22, - "ssh_username": "admin", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "manager_ids": [(4, self.manager1.id)], - } - ) - - # Remove Manager1 from manager_ids - record3.write({"manager_ids": [(5, 0, 0)]}) - - # Manager1 should not be able to delete their record after being removed - with self.assertRaises(AccessError): - record3.with_user(self.manager1).unlink() - - def test_server_template_reference_update(self): - """Test server template reference update cascades to dependent models""" - # 1. Add a variable value to server_template_sample - variable_value = self.VariableValue.create( - { - "variable_id": self.variable_os.id, - "value_char": "Ubuntu 20.04", - "server_template_id": self.server_template_sample.id, - } - ) - - # Store original references for comparison - original_template_reference = self.server_template_sample.reference - original_variable_value_reference = variable_value.reference - - # 2. Change the reference for server_template_sample to "super_template" - self.server_template_sample.write({"reference": "super_template"}) - - # 3. Verify that references are updated for dependent models - # Invalidate models to refresh all references - self.env["cx.tower.server.template"].invalidate_model(["reference"]) - self.env["cx.tower.variable.value"].invalidate_model(["reference"]) - - # Check that server template reference was updated - self.assertEqual(self.server_template_sample.reference, "super_template") - self.assertNotEqual( - self.server_template_sample.reference, original_template_reference - ) - - # Check that variable value reference was updated - # to include the new template reference - self.assertIn("super_template", variable_value.reference) - self.assertNotEqual(variable_value.reference, original_variable_value_reference) - - # Verify the reference pattern for variable value follows the expected format: - # ___ # noqa: E501 - expected_variable_pattern = ( - f"{self.variable_os.reference}_variable_value_server_template_" - f"{self.server_template_sample.reference}" - ) - self.assertEqual(variable_value.reference, expected_variable_pattern) diff --git a/addons/cetmix_tower_server/tests/test_shortcut.py b/addons/cetmix_tower_server/tests/test_shortcut.py deleted file mode 100644 index 804edf3..0000000 --- a/addons/cetmix_tower_server/tests/test_shortcut.py +++ /dev/null @@ -1,244 +0,0 @@ -from .common import TestTowerCommon - - -class TestTowerShortcut(TestTowerCommon): - """Test Tower Shortcut""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Server - cls.server_test_1_pro = cls.Server.create( - { - "name": "Test 1 Pro", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "p", - "skip_host_key": True, - } - ) - - # Variable - cls.variable_path_pro = cls.Variable.create({"name": "test_path_pro"}) - - # Command - cls.command_list_dir_pro = cls.Command.create( - { - "name": "Test create directory", - "code": "ls -l {{ test_path_ }}", - } - ) - - # Flight plan - cls.plan_1_pro = cls.Plan.create( - { - "name": "Test plan 1 Pro", - "note": "List directory contents", - } - ) - cls.plan_line_1_pro = cls.plan_line.create( - { - "sequence": 5, - "plan_id": cls.plan_1_pro.id, - "command_id": cls.command_list_dir_pro.id, - } - ) - - # Shortcuts - cls.shortcut_for_command = cls.Shortcut.create( - { - "name": "Shortcut for Command", - "action": "command", - "command_id": cls.command_list_dir_pro.id, - "server_ids": [(4, cls.server_test_1_pro.id)], - } - ) - - cls.shortcut_for_flight_plan = cls.Shortcut.create( - { - "name": "Shortcut for Flight Plan", - "action": "plan", - "plan_id": cls.plan_1_pro.id, - "server_ids": [(4, cls.server_test_1_pro.id)], - } - ) - - def test_shortcut_user_access_rules(self): - """Test shortcut user access rules""" - # Create shortcuts with different access levels and server/template assignments - shortcut_level_1_server = self.Shortcut.create( - { - "name": "Level 1 Server Shortcut", - "action": "command", - "command_id": self.command_list_dir_pro.id, - "server_ids": [(4, self.server_test_1_pro.id)], - "access_level": "1", - } - ) - - shortcut_level_2_template = self.Shortcut.create( - { - "name": "Level 2 Template Shortcut", - "action": "command", - "command_id": self.command_list_dir_pro.id, - "server_template_ids": [(4, self.server_template_sample.id)], - "access_level": "2", - } - ) - - # Remove bob from all cxtower_server groups - self.remove_from_group( - self.user_bob, - [ - "cetmix_tower_server.group_user", - "cetmix_tower_server.group_manager", - "cetmix_tower_server.group_root", - ], - ) - - shortcut_server_as_bob = shortcut_level_1_server.with_user(self.user_bob) - shortcut_template_as_bob = shortcut_level_2_template.with_user(self.user_bob) - - # Test: User access - self.add_to_group(self.user_bob, "cetmix_tower_server.group_user") - self.server_test_1_pro.write({"user_ids": [(4, self.user_bob.id)]}) - - # User should see level 1 shortcuts for their servers - res = shortcut_server_as_bob.read(["name"]) - self.assertEqual(res[0]["name"], shortcut_level_1_server.name) - - # User should NOT see level 2 shortcuts - search_result = shortcut_template_as_bob.search( - [("id", "=", shortcut_level_2_template.id)] - ) - self.assertEqual(len(search_result), 0) - - # Test: Manager access through server assignment - self.add_to_group(self.user_bob, "cetmix_tower_server.group_manager") - self.server_test_1_pro.write({"manager_ids": [(4, self.user_bob.id)]}) - - # Manager should see shortcuts for servers they manage - res = shortcut_server_as_bob.read(["name"]) - self.assertEqual(res[0]["name"], shortcut_level_1_server.name) - - # Manager should NOT see template shortcuts without template access - search_result = shortcut_template_as_bob.search( - [("id", "=", shortcut_level_2_template.id)] - ) - self.assertEqual(len(search_result), 0) - - # Test: Manager access through template assignment - self.server_template_sample.write({"manager_ids": [(4, self.user_bob.id)]}) - - # Manager should now see template shortcuts - res = shortcut_template_as_bob.read(["name"]) - self.assertEqual(res[0]["name"], shortcut_level_2_template.name) - - # Test: Manager access as template user - self.server_template_sample.write( - { - "manager_ids": [(3, self.user_bob.id)], # Remove from managers - "user_ids": [(4, self.user_bob.id)], # Add as user - } - ) - - # Manager should still see template shortcuts when they're a template user - res = shortcut_template_as_bob.read(["name"]) - self.assertEqual(res[0]["name"], shortcut_level_2_template.name) - - # Test: Root access to all shortcuts - shortcut_level_3 = self.Shortcut.create( - { - "name": "Level 3 Mixed Shortcut", - "action": "command", - "command_id": self.command_list_dir_pro.id, - "server_ids": [(4, self.server_test_1_pro.id)], - "server_template_ids": [(4, self.server_template_sample.id)], - "access_level": "3", - } - ) - shortcut_level_3_as_bob = shortcut_level_3.with_user(self.user_bob) - - # Manager should NOT see level 3 shortcuts - search_result = shortcut_level_3_as_bob.search( - [("id", "=", shortcut_level_3.id)] - ) - self.assertEqual(len(search_result), 0) - - # Root should see all shortcuts - self.add_to_group(self.user_bob, "cetmix_tower_server.group_root") - search_result = shortcut_level_3_as_bob.search( - [ - ( - "id", - "in", - [ - shortcut_level_1_server.id, - shortcut_level_2_template.id, - shortcut_level_3.id, - ], - ) - ] - ) - self.assertEqual(len(search_result), 3) - - def test_shortcut_run_type_command(self): - """Test run shortcut of type 'command'""" - self.shortcut_for_command.run(self.server_test_1_pro) - - # Check command log - shortcut_result = self.CommandLog.search( - [("command_id", "=", self.shortcut_for_command.command_id.id)] - ) - self.assertEqual(len(shortcut_result), 1, "Must be single log record") - self.assertEqual( - shortcut_result.server_id, - self.server_test_1_pro, - "Server should match", - ) - - def test_shortcut_run_type_plan(self): - """Test run shortcut of type 'plan'""" - self.shortcut_for_flight_plan.run(self.server_test_1_pro) - - # Check shortcut log - shortcut_result = self.PlanLog.search( - [("plan_id", "=", self.shortcut_for_flight_plan.plan_id.id)] - ) - self.assertEqual(len(shortcut_result), 1, "Must be single log record") - self.assertEqual( - shortcut_result.server_id, - self.server_test_1_pro, - "Server should match", - ) - - def test_shortcut_run_from_context(self): - """Test running shortcut with server from context""" - # Create a test shortcut - shortcut = self.Shortcut.create( - { - "name": "Context Test Shortcut", - "action": "command", - "command_id": self.command_list_dir_pro.id, - "server_ids": [(4, self.server_test_1_pro.id)], - } - ) - - # Run with server_id in context - shortcut.with_context(server_id=self.server_test_1_pro.id).run() - - # Check command log was created - log_entries = self.CommandLog.search( - [ - ("command_id", "=", shortcut.command_id.id), - ("server_id", "=", self.server_test_1_pro.id), - ] - ) - self.assertEqual(len(log_entries), 1, "Should create a log entry") - self.assertEqual( - log_entries.server_id, - self.server_test_1_pro, - "Server should match", - ) diff --git a/addons/cetmix_tower_server/tests/test_tag.py b/addons/cetmix_tower_server/tests/test_tag.py deleted file mode 100644 index 41b61a4..0000000 --- a/addons/cetmix_tower_server/tests/test_tag.py +++ /dev/null @@ -1,91 +0,0 @@ -from odoo.exceptions import AccessError, ValidationError - -from .common import TestTowerCommon - - -class TestTowerTag(TestTowerCommon): - """Test for the 'cx.tower.tag' model""" - - def test_01_unlink_as_user_with_used_tag(self): - """Test that user cannot delete tag that is in use""" - # Create test tag - test_tag = self.Tag.create( - { - "name": "Test Tag User", - } - ) - # Link tag to server - self.server_test_1.write({"tag_ids": [(4, test_tag.id)]}) - - with self.assertRaises(ValidationError): - test_tag.with_user(self.user).unlink() - - def test_02_unlink_as_user_with_unused_tag(self): - """Test that user cannot delete tag even if it's not in use""" - # Create new unused tag - unused_tag = self.Tag.create( - { - "name": "Unused Tag", - } - ) - # Try to delete unused tag - with self.assertRaises(AccessError): - unused_tag.with_user(self.user).unlink() - - def test_03_unlink_as_manager_with_used_tag(self): - """Test that manager cannot delete tag that is in use""" - # Create test tag as manager - test_tag = self.Tag.with_user(self.manager).create( - { - "name": "Test Tag Manager", - } - ) - # Link tag to server - test_tag.write({"server_ids": [(4, self.server_test_1.id)]}) - - # Access error because user doesn't have access to server - with self.assertRaises(AccessError): - test_tag.with_user(self.user).unlink() - - # Add 'manager' to server - self.server_test_1.write({"user_ids": [(4, self.manager.id)]}) - - # Validation error - with self.assertRaises(ValidationError): - test_tag.with_user(self.manager).unlink() - - def test_04_unlink_as_manager_with_own_tag(self): - """Test that manager can delete their own unused tag""" - # Create new unused tag as manager - unused_tag = self.Tag.with_user(self.manager).create( - { - "name": "Manager's Tag", - } - ) - # Manager should be able to delete their own unused tag - unused_tag.with_user(self.manager).unlink() - - def test_05_unlink_as_manager_with_other_tag(self): - """Test that manager cannot delete tag created by other user""" - # Create tag as root - other_tag = self.Tag.create( - { - "name": "Other's Tag", - } - ) - # Manager should not be able to delete tag created by other user - with self.assertRaises(AccessError): - other_tag.with_user(self.manager).unlink() - - def test_06_unlink_as_sudo(self): - """Test that sudo can delete tag that is in use""" - # Create test tag - test_tag = self.Tag.create( - { - "name": "Test Tag Sudo", - } - ) - # Link tag to server - self.server_test_1.write({"tag_ids": [(4, test_tag.id)]}) - - test_tag.with_user(self.user).sudo().unlink() diff --git a/addons/cetmix_tower_server/tests/test_tag_mixin.py b/addons/cetmix_tower_server/tests/test_tag_mixin.py deleted file mode 100644 index 8faa270..0000000 --- a/addons/cetmix_tower_server/tests/test_tag_mixin.py +++ /dev/null @@ -1,167 +0,0 @@ -from .common import TestTowerCommon - - -class TestTowerTagMixin(TestTowerCommon): - """Test class for tower tag mixin.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - # Create 3 tags to test tag mixin - cls.tag_test_1 = cls.Tag.create( - { - "name": "Test Tag 1", - } - ) - cls.tag_test_2 = cls.Tag.create( - { - "name": "Test Tag 2", - } - ) - cls.tag_test_3 = cls.Tag.create( - { - "name": "Test Tag 3", - } - ) - - # Create 3 commands to test tag mixin - cls.command_test_1 = cls.Command.create( - { - "name": "Test Command 1", - } - ) - cls.command_test_2 = cls.Command.create( - { - "name": "Test Command 2", - } - ) - cls.command_test_3 = cls.Command.create( - { - "name": "Test Command 3", - } - ) - - cls.all_commands = cls.command_test_1 | cls.command_test_2 | cls.command_test_3 - - # Add tags to commands - # - Command 1: Test Tag 1, Test Tag 2 - cls.command_test_1.add_tags(["Test Tag 1", "Test Tag 2", "Test Tag 3"]) - # - Command 2: Test Tag 2, Test Tag 3 - cls.command_test_2.add_tags(["Test Tag 2", "Test Tag 3"]) - # - Command 3: Test Tag 3 - cls.command_test_3.add_tags(["Test Tag 3"]) - - def test_01_add_tags(self): - """Test that tags are added to the record""" - self.assertEqual(len(self.command_test_1.tag_ids), 3) - self.assertEqual(len(self.command_test_2.tag_ids), 2) - self.assertEqual(len(self.command_test_3.tag_ids), 1) - self.assertIn(self.tag_test_1, self.command_test_1.tag_ids) - self.assertIn(self.tag_test_2, self.command_test_1.tag_ids) - self.assertIn(self.tag_test_3, self.command_test_1.tag_ids) - self.assertIn(self.tag_test_2, self.command_test_2.tag_ids) - self.assertIn(self.tag_test_3, self.command_test_2.tag_ids) - self.assertIn(self.tag_test_3, self.command_test_3.tag_ids) - - # Test adding duplicate tags (should be idempotent) - self.command_test_1.add_tags(["Test Tag 1"]) - self.assertEqual(len(self.command_test_1.tag_ids), 3) - - # Test adding single tag name - self.command_test_1.add_tags("Test Tag 1") - self.assertEqual(len(self.command_test_1.tag_ids), 3) - self.assertIn(self.tag_test_1, self.command_test_1.tag_ids) - self.assertIn(self.tag_test_2, self.command_test_1.tag_ids) - self.assertIn(self.tag_test_3, self.command_test_1.tag_ids) - - # Test adding invalid type (should return True) - self.assertTrue(self.command_test_1.add_tags(123)) - self.assertTrue(self.command_test_1.add_tags([])) - # Test adding invalid type (should return True) - # Empty list is a no-op - before = len(self.command_test_1.tag_ids) - self.assertTrue(self.command_test_1.add_tags([])) - self.assertEqual(len(self.command_test_1.tag_ids), before) - - # Test adding non-existent tags (should be ignored) - initial_count = len(self.command_test_1.tag_ids) - self.command_test_1.add_tags(["Non Existent Tag"]) - self.assertEqual(len(self.command_test_1.tag_ids), initial_count) - - def test_02_remove_tags(self): - """Test that tags are removed from the record""" - self.command_test_1.remove_tags(["Test Tag 1", "Test Tag 2"]) - self.assertEqual(len(self.command_test_1.tag_ids), 1) - - # Test removing single tag name - self.command_test_2.remove_tags("Test Tag 2") - self.assertEqual(len(self.command_test_2.tag_ids), 1) - self.assertIn(self.tag_test_3, self.command_test_2.tag_ids) - - # Test removing invalid type (should return True) - self.assertTrue(self.command_test_1.remove_tags(123)) - # Test removing no tags (should return True) - self.assertTrue(self.command_test_1.remove_tags([])) - - def test_03_has_tags(self): - """Test that the record has any of the given tags""" - - # Search selected records - commands_with_any_tags = self.all_commands.has_tags( - ["Test Tag 1", "Test Tag 2"] - ) - self.assertEqual(len(commands_with_any_tags), 2) - self.assertIn(self.command_test_1, commands_with_any_tags) - self.assertIn(self.command_test_2, commands_with_any_tags) - - # Search all records in the model - commands_with_any_tags = self.Command.has_tags( - ["Test Tag 1", "Test Tag 2"], search_all=True - ) - self.assertEqual(len(commands_with_any_tags), 2) - self.assertIn(self.command_test_1, commands_with_any_tags) - self.assertIn(self.command_test_2, commands_with_any_tags) - - # Search with single tag name - commands_with_any_tags = self.all_commands.has_tags("Test Tag 2") - self.assertEqual(len(commands_with_any_tags), 2) - self.assertIn(self.command_test_1, commands_with_any_tags) - self.assertIn(self.command_test_2, commands_with_any_tags) - - commands_with_any_tags = self.Command.has_tags("Test Tag 2", search_all=True) - self.assertEqual(len(commands_with_any_tags), 2) - self.assertIn(self.command_test_1, commands_with_any_tags) - self.assertIn(self.command_test_2, commands_with_any_tags) - - # Search with invalid type (should return empty recordset) - commands_with_any_tags = self.Command.has_tags(123) - self.assertEqual(len(commands_with_any_tags), 0) - - # Search with no tags (should return empty recordset) - commands_with_any_tags = self.Command.has_tags([]) - self.assertEqual(len(commands_with_any_tags), 0) - - def test_04_has_all_tags(self): - """Test that the record has all of the given tags""" - - # Search selected records - commands_with_all_tags = self.all_commands.has_all_tags( - ["Test Tag 1", "Test Tag 2"] - ) - self.assertEqual(len(commands_with_all_tags), 1) - self.assertIn(self.command_test_1, commands_with_all_tags) - - # Search all records in the model - commands_with_all_tags = self.Command.has_all_tags( - ["Test Tag 1", "Test Tag 2"], search_all=True - ) - self.assertEqual(len(commands_with_all_tags), 1) - self.assertIn(self.command_test_1, commands_with_all_tags) - - # Search with invalid type (should return empty recordset) - commands_with_all_tags = self.Command.has_all_tags(123) - self.assertEqual(len(commands_with_all_tags), 0) - - # Search with no tags (should return empty recordset) - commands_with_all_tags = self.Command.has_all_tags([]) - self.assertEqual(len(commands_with_all_tags), 0) diff --git a/addons/cetmix_tower_server/tests/test_tools.py b/addons/cetmix_tower_server/tests/test_tools.py deleted file mode 100644 index 98cd545..0000000 --- a/addons/cetmix_tower_server/tests/test_tools.py +++ /dev/null @@ -1,38 +0,0 @@ -from odoo.tests import common - -from ..models.tools import CHARS, generate_random_id - - -class TestTools(common.TransactionCase): - """Test class for tools module.""" - - def test_generate_random_id(self): - """Test random id generation""" - # Test single section - result = generate_random_id() - self.assertEqual(len(result), 4) # Default length is 4 - self.assertTrue(all(c in CHARS for c in result)) # All chars from CHARS - - # Test multiple sections - result = generate_random_id(sections=2) - sections = result.split("-") - self.assertEqual(len(sections), 2) - self.assertTrue(all(len(s) == 4 for s in sections)) - self.assertTrue(all(c in CHARS for s in sections for c in s)) - - # Test custom population - result = generate_random_id(population=6) - self.assertEqual(len(result), 6) - - # Test custom separator - result = generate_random_id(sections=2, separator="_") - self.assertIn("_", result) - self.assertEqual(len(result.split("_")), 2) - - # Test invalid inputs - self.assertIsNone(generate_random_id(sections=0)) - self.assertIsNone(generate_random_id(population=-1)) - - # Test empty separator - result = generate_random_id(sections=3, separator="") - self.assertEqual(len(result), 12) # 3 sections of 4 chars with no separator diff --git a/addons/cetmix_tower_server/tests/test_update_related_variable_names.py b/addons/cetmix_tower_server/tests/test_update_related_variable_names.py deleted file mode 100644 index aba26cd..0000000 --- a/addons/cetmix_tower_server/tests/test_update_related_variable_names.py +++ /dev/null @@ -1,204 +0,0 @@ -from .common import TestTowerCommon - - -class TestUpdateRelatedVariableNames(TestTowerCommon): - """Test Update Related Variable Names""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create test variables - cls.var1 = cls.Variable.create({"name": "var1", "reference": "var1"}) - cls.var2 = cls.Variable.create({"name": "var2", "reference": "var2"}) - cls.var3 = cls.Variable.create({"name": "var3", "reference": "var3"}) - - cls.test_command = cls.Command.create( - { - "name": "Test Command", - "code": "{{ var1 }} and {{ var2 }}", - "path": "{{ var3 }}", - } - ) - - cls.server = cls.Server.create( - { - "name": "Test Server", - "color": 2, - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "ssh_auth_mode": "k", - "ssh_key_id": cls.key_1.id, - } - ) - cls.test_file = cls.File.create( - { - "server_id": cls.server.id, - "code": "{{ var1 }} is used", - "server_dir": "path/to/{{ var2 }}", - "name": "{{ var3 }}.txt", - } - ) - - cls.test_plan_line = cls.plan_line.create( - { - "command_id": cls.test_command.id, - "condition": "Condition with {{ var1 }} and {{ var2 }}", - } - ) - - cls.test_variable_value = cls.VariableValue.create( - { - "variable_id": cls.variable_os.id, - "value_char": "{{ var1 }} is here and {{ var2 }} too", - } - ) - - cls.test_file_template = cls.FileTemplate.create( - { - "name": "Test File Template", - "code": "{{ var1 }} in code", - "server_dir": "This path has {{ var2 }}", - "file_name": "file_name_with_{{ var1 }}", - } - ) - - def test_variables_command_computation(self): - """ - Test that the variable_ids field is correctly computed based on the 'code' - and 'path' fields of the command. - """ - # Verify that the correct variables are assigned to variable_ids - self.assertEqual( - set(self.test_command.variable_ids.ids), - {self.var1.id, self.var2.id, self.var3.id}, - "The variable_ids should contain var1, var2, and var3.", - ) - - def test_variables_command_clearing(self): - """ - Test that the variable_ids field is cleared when - no variables are found in the code or path. - """ - # Update code and path to remove references - self.test_command.write( - {"code": "No variables here", "path": "No variables here either"} - ) - # Verify that variable_ids is empty - self.assertFalse( - self.test_command.variable_ids, - "The variable_ids should be empty when no variables are found.", - ) - - def test_variables_file_computation(self): - """ - Test that the variable_ids field is correctly computed based on the 'code', - 'server_dir', and 'name' fields of the file. - """ - # Verify that the correct variables are assigned to variable_ids - self.assertEqual( - set(self.test_file.variable_ids.ids), - {self.var1.id, self.var2.id, self.var3.id}, - "The variable_ids should contain var1, var2, and var3.", - ) - - def test_variables_file_clearing(self): - """ - Test that the variable_ids field is cleared when - no variables are found in the code, server_dir, or name fields. - """ - # Update the file to remove references - self.test_file.write( - { - "code": "No variables here", - "server_dir": "No variables here either", - "name": "no_var.txt", - } - ) - # Verify that variable_ids is empty - self.assertFalse( - self.test_file.variable_ids, - "The variable_ids should be empty when no variables are found.", - ) - - def test_variables_plan_line_computation(self): - """ - Test that the variable_ids field is correctly - computed based on the 'condition' field of the plan line. - """ - # Verify that the correct variables are assigned to variable_ids - self.assertEqual( - set(self.test_plan_line.variable_ids.ids), - {self.var1.id, self.var2.id}, - "The variable_ids should contain var1 and var2.", - ) - - def test_variables_plan_line_clearing(self): - """ - Test that the variable_ids field is cleared when - no variables are found in the condition field. - """ - # Update the plan line to remove references - self.test_plan_line.write({"condition": "No variables in this condition"}) - # Verify that variable_ids is empty - self.assertFalse( - self.test_plan_line.variable_ids, - "The variable_ids should be empty when no variables are found.", - ) - - def test_variables_variable_value_computation(self): - """ - Test that the variable_ids field is correctly - computed based on the 'value_char' field. - """ - # Verify that the correct variables are assigned to variable_ids - self.assertEqual( - set(self.test_variable_value.variable_ids.ids), - {self.var1.id, self.var2.id}, - "The variable_ids should contain var1 and var2.", - ) - - def test_variables_variable_value_clearing(self): - """ - Test that the variable_ids field is cleared when - no variables are found in the value_char field. - """ - # Update the variable value to remove references - self.test_variable_value.write({"value_char": "No variables in this text"}) - # Verify that variable_ids is empty - self.assertFalse( - self.test_variable_value.variable_ids, - "The variable_ids should be empty when no variables are found.", - ) - - def test_variables_file_template_computation(self): - """ - Test that the variable_ids field is correctly computed - based on 'code', 'server_dir', and 'file_name' fields. - """ - # Verify that the correct variables are assigned to variable_ids - self.assertEqual( - set(self.test_file_template.variable_ids.ids), - {self.var1.id, self.var2.id}, - "The variable_ids should contain var1 and var2.", - ) - - def test_variable_file_template_clearing(self): - """ - Test that the variable_ids field is cleared when - no variables are found in code, server_dir, or file_name. - """ - # Update the file template to remove references - self.test_file_template.write( - { - "code": "No variables here", - "server_dir": "No variables here either", - "file_name": "no_var_in_file", - } - ) - # Verify that variable_ids is empty - self.assertFalse( - self.test_file_template.variable_ids, - "The variable_ids should be empty when no variables are found.", - ) diff --git a/addons/cetmix_tower_server/tests/test_variable.py b/addons/cetmix_tower_server/tests/test_variable.py deleted file mode 100644 index 29838fd..0000000 --- a/addons/cetmix_tower_server/tests/test_variable.py +++ /dev/null @@ -1,1130 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from unittest.mock import patch - -from psycopg2 import IntegrityError - -from odoo import _, fields -from odoo.exceptions import AccessError, ValidationError -from odoo.tests.common import Form -from odoo.tools.misc import mute_logger - -from .common import TestTowerCommon - - -class TestTowerVariable(TestTowerCommon): - """Testing variables and variable values.""" - - def check_variable_values(self, vals, server_ids=None): - """Check if variable values are correctly stored in db - - Args: - vals (List of tuples): format ("variable_id", "value") - server_id (cx.tower.server()): Servers those variables belong to. - """ - if server_ids: - variable_records = server_ids.variable_value_ids - else: - variable_records = self.VariableValue.search([("is_global", "=", True)]) - len_vals = len(vals) - - # Ensure correct number of records - self.assertEqual( - len(variable_records), len_vals, msg="Must be %s records" % str(len_vals) - ) - - # Check variable values - for val in vals: - variable_line = variable_records.filtered( - lambda v, val=val: v.variable_id.id == val[0] - ) - self.assertEqual( - len(variable_line), 1, msg="Must be a single variable line" - ) - expected_value = val[1] or False - self.assertEqual( - variable_line.value_char, - expected_value, - msg="Variable value does not match provided one", - ) - - def test_variable_values(self): - """Test common variable operations""" - - # -- 1 -- - # Server specific variables - - # Add two variables - with Form(self.server_test_1) as f: - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_dir - line.value_char = "/opt/odoo" - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_url - line.value_char = "example.com" - f.save() - - vals = [ - (self.variable_url.id, "example.com"), - (self.variable_dir.id, "/opt/odoo"), - ] - self.check_variable_values(vals=vals, server_ids=self.server_test_1) - - # Add another variable and edit the existing one - with Form(self.server_test_1) as f: - with f.variable_value_ids.edit(1) as line: - line.value_char = "meme.example.com" - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_version - line.value_char = "10.0" - f.save() - - vals = [ - (self.variable_url.id, "meme.example.com"), - (self.variable_dir.id, "/opt/odoo"), - (self.variable_version.id, "10.0"), - ] - self.check_variable_values(vals=vals, server_ids=self.server_test_1) - - # Delete two variables, add a new one - with Form(self.server_test_1) as f: - f.variable_value_ids.remove(index=0) - f.variable_value_ids.remove(index=0) - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_os - line.value_char = "Debian" - - # Add an empty variable value - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_url - f.save() - - vals = [ - (self.variable_os.id, "Debian"), - (self.variable_version.id, "10.0"), - (self.variable_url.id, False), - ] - self.check_variable_values(vals=vals, server_ids=self.server_test_1) - - # Test 'get_variable_values' function - res = self.server_test_1.get_variable_values( - ["test_dir", "test_os", "test_url", "test_version"] - ) - self.assertEqual(len(res), 1, "Must be a single record key in the result") - - res_vars = res.get(self.server_test_1.id) - var_dir = res_vars["test_dir"] - var_os = res_vars["test_os"] - var_url = res_vars["test_url"] - var_version = res_vars["test_version"] - - self.assertIsNone(var_dir, msg="Variable 'dir' must be None") - self.assertFalse(var_url, msg="Variable 'url' must be False") - self.assertEqual(var_os, "Debian", msg="Variable 'os' must be 'Debian'") - self.assertEqual(var_version, "10.0", msg="Variable 'version' must be '10.0'") - - # -- 2 -- - # Test global variable values - - # Create a global value for the 'dir' variable - self.VariableValue.create( - {"variable_id": self.variable_dir.id, "value_char": "/global/dir"} - ) - res = self.server_test_1.get_variable_values( - ["test_dir", "test_os", "test_url", "test_version"] - ) - self.assertEqual(len(res), 1, "Must be a single record key in the result") - - res_vars = res.get(self.server_test_1.id) - var_dir = res_vars["test_dir"] - var_os = res_vars["test_os"] - var_url = res_vars["test_url"] - var_version = res_vars["test_version"] - - self.assertEqual( - var_dir, "/global/dir", msg="Variable 'dir' must be equal to '/global/dir'" - ) - self.assertFalse(var_url, msg="Variable 'url' must be False") - self.assertEqual(var_os, "Debian", msg="Variable 'os' must be 'Debian'") - self.assertEqual(var_version, "10.0", msg="Variable 'version' must be '10.0'") - - # Now save a local value for the variable - with Form(self.server_test_1) as f: - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_dir - line.value_char = "/opt/odoo" - f.save() - - # Check - res = self.server_test_1.get_variable_values( - ["test_dir", "test_os", "test_url", "test_version"] - ) - self.assertEqual(len(res), 1, "Must be a single record key in the result") - - res_vars = res.get(self.server_test_1.id) - var_dir = res_vars["test_dir"] - var_os = res_vars["test_os"] - var_url = res_vars["test_url"] - var_version = res_vars["test_version"] - - self.assertEqual( - var_dir, "/opt/odoo", msg="Variable 'dir' must be equal to '/opt/odoo'" - ) - self.assertFalse(var_url, msg="Variable 'url' must be False") - self.assertEqual(var_os, "Debian", msg="Variable 'os' must be 'Debian'") - self.assertEqual(var_version, "10.0", msg="Variable 'version' must be '10.0'") - - def test_variables_in_variable_values(self): - """Test variables in variable values - eg - home: /home - user: bob - home_dir: {{ home }}/{{ user }} --> /home/bob - """ - - # Add local variables - with Form(self.server_test_1) as f: - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_dir - line.value_char = "/web" - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_path - line.value_char = "{{ test_dir }}/{{ test_version }}" - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_url - line.value_char = "{{ test_path_ }}/example.com" - f.save() - - # Create a global value for the 'Version' variable - self.VariableValue.create( - {"variable_id": self.variable_version.id, "value_char": "10.0"} - ) - - # Check values - res = self.server_test_1.get_variable_values( - ["test_dir", "test_url", "test_version"] - ) - self.assertEqual(len(res), 1, "Must be a single record key in the result") - - res_vars = res.get(self.server_test_1.id) - var_dir = res_vars["test_dir"] - var_url = res_vars["test_url"] - var_version = res_vars["test_version"] - - self.assertEqual(var_dir, "/web", msg="Variable 'dir' must be '/web'") - self.assertEqual( - var_url, - "/web/10.0/example.com", - msg="Variable 'url' must be '/web/10.0/example.com'", - ) - self.assertEqual(var_version, "10.0", msg="Variable 'version' must be '10.0'") - - def test_variable_values_unlink(self): - """Ensure variable values are deleted properly - - Create a new server - - Add 2 variable values - - Delete server - - Ensure variable values are deleted - """ - - def get_value_count(variable): - """helper function to count variable value records - Arg: (cx.tower.variable) variable rec - Returns: (int) record count - """ - return self.VariableValue.search_count([("variable_id", "=", variable.id)]) - - # Get variable values count before adding variables to server - count_dir_before = get_value_count(self.variable_dir) - count_url_before = get_value_count(self.variable_url) - - # Create new server - server_test_var = self.Server.create( - { - "name": "Test Var", - "os_id": self.os_debian_10.id, - "ip_v4_address": "localhost", - "ssh_username": "bob", - "ssh_password": "pass", - } - ) - - # Add two variables to server - with Form(server_test_var) as f: - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_dir - line.value_char = "/opt/odoo" - with f.variable_value_ids.new() as line: - line.variable_id = self.variable_url - line.value_char = "example.com" - f.save() - - # Number of values should be incremented - self.assertEqual( - get_value_count(self.variable_dir), - count_dir_before + 1, - msg="Value count must be incremented!", - ) - self.assertEqual( - get_value_count(self.variable_url), - count_url_before + 1, - msg="Value count must be incremented!", - ) - - # Delete the server - server_test_var.unlink() - self.assertEqual( - get_value_count(self.variable_dir), - count_dir_before, - msg="Value count must be same as before server creation!", - ) - self.assertEqual( - get_value_count(self.variable_url), - count_url_before, - msg="Value count must be same as before server creation!", - ) - - def test_variable_value_toggle_global(self): - """Test what happens when variable value 'global' setting is togged""" - - variable_meme = self.Variable.create({"name": "meme"}) - variable_value_pepe = self.VariableValue.create( - {"variable_id": variable_meme.id, "value_char": "Pepe"} - ) - - self.assertEqual( - variable_value_pepe.is_global, True, msg="Value 'Pepe' must be global" - ) - - # Test `_check_is_global` function - self.assertEqual( - variable_value_pepe._check_is_global(), - True, - msg="Value 'Pepe' must be global", - ) - - # Try to create another global value for the same variable - with self.assertRaises(ValidationError) as err: - self.VariableValue.create( - {"variable_id": variable_meme.id, "value_char": "Doge"} - ) - - # We check the message in order to ensure that - # exception was raised by the correct event. - self.assertEqual( - err.exception.args[0], - _("Only one global value can be defined for variable 'meme'"), - msg="Error message doesn't match. Check if you have modified it in code:" - "models/cx_tower_server.py", - ) - - # Try to disable 'global' for a global variable explicitly - with self.assertRaises(ValidationError) as err: - variable_value_pepe.is_global = False - - # We check the message in order to ensure that - # exception was raised by the correct event. - self.assertEqual( - err.exception.args[0], - _( - "Cannot change 'global' status for " - "'meme' with value 'Pepe'." - "\nTry to assigns it to a record instead." - ), - msg="Error message doesn't match. Check if you have modified it in code:" - "models/cx_tower_server.py", - ) - - def test_system_variable_server_type_values(self): - """Test system variables of `server` type""" - - # Modify server record for testing - self.server_test_1.ip_v6_address = "suchmuchipv6" - self.server_test_1.url = "meme.example.com" - self.server_test_1.partner_id = ( - self.env["res.partner"].create({"name": "Pepe Frog"}).id - ) - - # Create new command with system variables - command = self.Command.create( - { - "name": "Super System Command", - "code": "echo {{ tower.server.name }} " - "{{ tower.server.username}} " - "{{ tower.server.partner_name }} " - "{{ tower.server.ipv4 }} " - "{{ tower.server.ipv6 }} " - "{{ tower.server.url }} ", - } - ) - - # Get variables - variables = command.get_variables().get(str(command.id)) - # Get variable values - variable_values = self.server_test_1.get_variable_values(variables).get( - self.server_test_1.id - ) - - # Check values - self.assertEqual( - variable_values["tower"]["server"]["name"], - self.server_test_1.name, - "System variable doesn't match server property", - ) - self.assertEqual( - variable_values["tower"]["server"]["reference"], - self.server_test_1.reference, - "System variable doesn't match server property", - ) - self.assertEqual( - variable_values["tower"]["server"]["username"], - self.server_test_1.ssh_username, - "System variable doesn't match server property", - ) - self.assertEqual( - variable_values["tower"]["server"]["username"], - self.server_test_1.ssh_username, - "System variable doesn't match server property", - ) - self.assertEqual( - variable_values["tower"]["server"]["partner_name"], - self.server_test_1.partner_id.name, - "System variable doesn't match server property", - ) - self.assertEqual( - variable_values["tower"]["server"]["ipv4"], - self.server_test_1.ip_v4_address, - "System variable doesn't match server property", - ) - self.assertEqual( - variable_values["tower"]["server"]["ipv6"], - self.server_test_1.ip_v6_address, - "System variable doesn't match server property", - ) - self.assertEqual( - variable_values["tower"]["server"]["url"], - self.server_test_1.url, - "System variable doesn't match server property", - ) - - @patch( - "odoo.addons.cetmix_tower_server.models.cx_tower_variable_mixin.fields.Datetime.now", - return_value=fields.Datetime.now(), - ) - @patch( - "odoo.addons.cetmix_tower_server.models.cx_tower_variable_mixin.fields.Date.today", - return_value=fields.Date.today(), - ) - @patch( - "odoo.addons.cetmix_tower_server.models.cx_tower_variable_mixin.uuid.uuid4", - return_value="suchmuchuuid4", - ) - def test_system_variable_tools_type_values(self, mock_uuid4, mock_today, mock_now): - """Test system variables of `tools` type""" - - # Create new command with system variables - command = self.Command.create( - {"name": "Super System Command", "code": "echo {{ tower.tools.uuid}}"} - ) - - # Get variables - variables = command.get_variables().get(str(command.id)) - # Get variable values - variable_values = self.server_test_1.get_variable_values(variables).get( - self.server_test_1.id - ) - - # Check values - self.assertEqual( - variable_values["tower"]["tools"]["uuid"], - mock_uuid4.return_value, - "System variable doesn't match result provided by tools", - ) - self.assertEqual( - variable_values["tower"]["tools"]["today"], - str(mock_today.return_value), - "System variable doesn't match result provided by tools", - ) - self.assertEqual( - variable_values["tower"]["tools"]["now"], - str(mock_now.return_value), - "System variable doesn't match result provided by tools", - ) - self.assertEqual( - variable_values["tower"]["tools"]["today_underscore"], - str(mock_today.return_value) - .replace("-", "_") - .replace(" ", "_") - .replace(":", "_") - .replace(".", "_") - .replace("/", "_"), - "System variable doesn't match result provided by tools", - ) - self.assertEqual( - variable_values["tower"]["tools"]["now_underscore"], - str(mock_now.return_value) - .replace("-", "_") - .replace(":", "_") - .replace(" ", "_") - .replace(".", "_") - .replace("/", "_"), - "System variable doesn't match result provided by tools", - ) - - def test_make_value_pythonic(self): - """Test making variable values 'pythonic`""" - - # Number - value = 12.34 - expected_value = '"12.34"' - result_value = self.Command._make_value_pythonic(value) - - self.assertEqual( - expected_value, result_value, "Result value doesn't match expected" - ) - - # Text - value = "Doge much like" - expected_value = '"Doge much like"' - result_value = self.Command._make_value_pythonic(value) - - self.assertEqual( - expected_value, result_value, "Result value doesn't match expected" - ) - - # Boolean - value = True - expected_value = True - result_value = self.Command._make_value_pythonic(value) - - self.assertEqual( - expected_value, result_value, "Result value doesn't match expected" - ) - - # None - value = None - expected_value = None - result_value = self.Command._make_value_pythonic(value) - - self.assertEqual( - expected_value, result_value, "Result value doesn't match expected" - ) - - # Dict - value = {"doge": {"likes": "memes", "much": 200}} - expected_value = {"doge": {"likes": '"memes"', "much": '"200"'}} - result_value = self.Command._make_value_pythonic(value) - - self.assertEqual( - expected_value, result_value, "Result value doesn't match expected" - ) - - def test_get_by_variable_reference(self): - """Test getting variable values by variable reference""" - - variable_meme = self.Variable.create( - {"name": "Meme Variable", "reference": "meme_variable"} - ) - global_value = self.VariableValue.create( - {"variable_id": variable_meme.id, "value_char": "Memes Globalvs"} - ) - - # -- 1 -- Get value for Server with no server value defined - server_result = self.VariableValue.get_by_variable_reference( - variable_meme.reference, server_id=self.server_test_1.id - ) - self.assertIsNone(server_result.get("server")) - self.assertIsNone(server_result.get("server_template")) - self.assertEqual(server_result.get("global"), global_value.value_char) - - # -- 2 -- Add server value and try again - server_value = self.VariableValue.create( - { - "variable_id": variable_meme.id, - "value_char": "Memes Servervs", - "server_id": self.server_test_1.id, - } - ) - server_result = self.VariableValue.get_by_variable_reference( - variable_meme.reference, server_id=self.server_test_1.id - ) - self.assertEqual(server_result.get("server"), server_value.value_char) - self.assertEqual(server_result.get("global"), global_value.value_char) - self.assertIsNone(server_result.get("server_template")) - - # -- 3 -- Do not fetch global value now - server_result = self.VariableValue.get_by_variable_reference( - variable_meme.reference, server_id=self.server_test_1.id, check_global=False - ) - self.assertIsNone(server_result.get("global")) - self.assertEqual(server_result.get("server"), server_value.value_char) - self.assertIsNone(server_result.get("server_template")) - - # -- 4 -- Check server template value - server_template_value = self.VariableValue.create( - { - "variable_id": variable_meme.id, - "value_char": "Memes Servervs Templatvs", - "server_template_id": self.server_template_sample.id, - } - ) - server_result = self.VariableValue.get_by_variable_reference( - variable_meme.reference, server_template_id=self.server_template_sample.id - ) - self.assertEqual(server_result.get("global"), global_value.value_char) - self.assertIsNone(server_result.get("server")) - self.assertEqual( - server_result.get("server_template"), server_template_value.value_char - ) - - def test_single_assignment(self): - """Test that a variable can only be assigned to one model at a time.""" - # Create a variable value assigned to the server - variable_value = self.env["cx.tower.variable.value"].create( - { - "variable_id": self.variable_os.id, - "value_char": "Branch = Main", - "server_id": self.server_test_1.id, - } - ) - - # Try to assign the same variable value to - # server template and expect a ValidationError - with self.assertRaises(ValidationError): - variable_value.write({"server_template_id": self.server_template_sample.id}) - - # Try to assign the same variable value to - # plan line action and expect a ValidationError - with self.assertRaises(ValidationError): - variable_value.write({"plan_line_action_id": self.plan_line_1_action_1.id}) - - def test_unique_assignment(self): - """Test that the same variable value cannot be - assigned multiple times to the same record. - """ - - # Create a variable - variable = self.env["cx.tower.variable"].create( - {"name": "Environment Type", "note": "The environment type for the server."} - ) - - # Create a server - server = self.env["cx.tower.server"].create( - { - "name": "Test Server", - "ip_v4_address": "127.0.0.1", - "ssh_username": "testuser", - "ssh_password": "testpassword", - "ssh_auth_mode": "p", - } - ) - - # Create a variable value for the server - self.env["cx.tower.variable.value"].create( - { - "variable_id": variable.id, - "value_char": "Production", - "server_id": server.id, - } - ) - - # Try to create a second variable value with the same variable and server - with mute_logger("odoo.sql_db"), self.assertRaises( - IntegrityError, - msg="A variable value cannot be assigned multiple times to the same server", - ): - self.env["cx.tower.variable.value"].create( - { - "variable_id": variable.id, - "value_char": "Production", - "server_id": server.id, - } - ) - - def test_value_access_level_consistency(self): - """Test that variable value access level cannot be lower - than variable access level.""" - - # Create test servers - server_2 = self.Server.create( - { - "name": "Test Server 2", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "os_id": self.os_debian_10.id, - } - ) - - server_3 = self.Server.create( - { - "name": "Test Server 3", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "os_id": self.os_debian_10.id, - } - ) - - # Create a variable with access level "2" - variable_restricted = self.Variable.create( - { - "name": "restricted_variable", - "access_level": "2", - } - ) - - # Should succeed: value with same access level as variable - try: - self.VariableValue.create( - { - "variable_id": variable_restricted.id, - "value_char": "test_value1", - "access_level": "2", - "is_global": True, - } - ) - except ValidationError: - self.fail("Should allow creating value with same access level as variable") - - # Should succeed: value with higher access level than variable - try: - self.VariableValue.create( - { - "variable_id": variable_restricted.id, - "value_char": "test_value2", - "access_level": "3", - "server_id": server_2.id, - } - ) - except ValidationError: - self.fail( - "Should allow creating value with higher access level than variable" - ) - - # Should fail: value with lower access level than variable - with self.assertRaises( - ValidationError, - msg="Should not allow creating value with lower access level than variable", - ): - self.VariableValue.create( - { - "variable_id": variable_restricted.id, - "value_char": "test_value3", - "access_level": "1", - "server_id": server_3.id, - } - ) - - # Test updating existing value's access level - value = self.VariableValue.create( - { - "variable_id": self.variable_dir.id, # Using a different variable - "value_char": "test_value4", - "access_level": "2", - "server_id": server_3.id, - } - ) - - # Should fail: updating to lower access level than variable - with self.assertRaises( - ValidationError, - msg="Should not allow updating value to lower access level than variable", - ): - value.write({"access_level": "1"}) - - # Should succeed: updating to higher access level than variable - try: - value.write({"access_level": "3"}) - except ValidationError: - self.fail( - "Should allow updating value to higher access level than variable" - ) - - def test_variable_access_rights(self): - """Test access rights for variables based on access levels and user roles.""" - - # Create variables with different access levels - variable_level_1 = self.Variable.create( - { - "name": "Level 1 Variable", - "access_level": "1", - } - ) - - variable_level_2 = self.Variable.create( - { - "name": "Level 2 Variable", - "access_level": "2", - } - ) - - variable_level_3 = self.Variable.create( - { - "name": "Level 3 Variable", - "access_level": "3", - } - ) - manager2 = self.Users.create( - { - "name": "Manager 2", - "login": "manager2@example.com", - "groups_id": [(4, self.group_manager.id)], - } - ) - - # Test User Access - # --------------- - # Should see level 1 variables - records = self.Variable.with_user(self.user).search( - [ - ( - "id", - "in", - [variable_level_1.id, variable_level_2.id, variable_level_3.id], - ) - ] - ) - self.assertEqual(len(records), 1, "User should only see level 1 variables") - self.assertEqual( - records.id, variable_level_1.id, "User should only see level 1 variables" - ) - - # Test Manager Access - # ----------------- - # Should see level 1 and 2 variables - records = self.Variable.with_user(self.manager).search( - [ - ( - "id", - "in", - [variable_level_1.id, variable_level_2.id, variable_level_3.id], - ) - ] - ) - self.assertEqual(len(records), 2, "Manager should see level 1 and 2 variables") - self.assertIn( - variable_level_1.id, records.ids, "Manager should see level 1 variables" - ) - self.assertIn( - variable_level_2.id, records.ids, "Manager should see level 2 variables" - ) - - # Test Manager Write Access - # ----------------------- - # Create a variable as manager - manager_variable = self.Variable.with_user(self.manager).create( - { - "name": "Manager Created Variable", - "access_level": "2", - } - ) - - # Manager should be able to modify their own variable - try: - manager_variable.with_user(self.manager).write({"name": "Updated Name"}) - except AccessError: - self.fail("Manager should be able to modify their own variables") - - # Manager should not be able to modify another manager's variable - manager2_variable = self.Variable.with_user(manager2).create( - { - "name": "Other Manager Variable", - "access_level": "2", - } - ) - - with self.assertRaises(AccessError): - manager2_variable.with_user(self.manager).write({"name": "Try Update"}) - - # Manager should not be able to create level 3 variable - with self.assertRaises(AccessError): - self.Variable.with_user(self.manager).create( - { - "name": "Try Level 3", - "access_level": "3", - } - ) - - # Test Root Access - # -------------- - # Root should see all variables - records = self.Variable.with_user(self.root).search( - [ - ( - "id", - "in", - [variable_level_1.id, variable_level_2.id, variable_level_3.id], - ) - ] - ) - self.assertEqual(len(records), 3, "Root should see all variables") - - # Root should be able to create any level variable - try: - self.Variable.with_user(self.root).create( - { - "name": "Root Level 3", - "access_level": "3", - } - ) - except AccessError: - self.fail("Root should be able to create any level variable") - - # Root should be able to modify any variable - try: - variable_level_3.with_user(self.root).write({"name": "Updated by Root"}) - except AccessError: - self.fail("Root should be able to modify any variable") - - def test_validate_value(self): - """Test variable value validation""" - # Create variable with validation pattern - variable_with_pattern = self.Variable.create( - { - "name": "Test Pattern", - "validation_pattern": "^[a-z0-9]+$", - "validation_message": "Only lowercase letters and numbers allowed", - } - ) - - # Test valid values - valid_value = "abc123" - is_valid, message = variable_with_pattern._validate_value(valid_value) - self.assertTrue(is_valid, "Value should be valid") - self.assertIsNone(message, "No message should be returned for valid value") - - # Test invalid values - invalid_value = "ABC123!" - is_valid, message = variable_with_pattern._validate_value(invalid_value) - self.assertFalse(is_valid, "Value should be invalid") - self.assertEqual( - message, - f"Variable: {variable_with_pattern.name}, Value: {invalid_value}\n" - "Only lowercase letters and numbers allowed", - "Invalid value message doesn't match", - ) - - # Test empty value - is_valid, message = variable_with_pattern._validate_value(None) - self.assertTrue(is_valid, "Empty value should be valid") - self.assertIsNone(message, "No message should be returned for empty value") - - # Test variable without pattern - variable_no_pattern = self.Variable.create( - { - "name": "No Pattern", - } - ) - test_value = "Any Value!" - is_valid, message = variable_no_pattern._validate_value(test_value) - self.assertTrue(is_valid, "Value should be valid when no pattern is set") - self.assertIsNone( - message, "No message should be returned when no pattern is set" - ) - - # Test default validation message - variable_default_message = self.Variable.create( - { - "name": "Default Message", - "validation_pattern": "^[a-z]+$", - } - ) - invalid_value = "123" - is_valid, message = variable_default_message._validate_value(invalid_value) - self.assertFalse(is_valid, "Value should be invalid") - self.assertEqual( - message, - f"Variable: {variable_default_message.name}, Value: {invalid_value}\n" - f"{variable_default_message.DEFAULT_VALIDATION_MESSAGE}", - "Default validation message doesn't match", - ) - - -class TestVariableReferenceRename(TestTowerCommon): - """Ensure variable rename updates all Jinja references using shared fixtures.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - cls.ref_old = cls.variable_version.reference - cls.ref_new = "software_version" - - cls.command = cls.Command.create( - { - "name": "Show version (test)", - "code": f"echo {{ {{ {cls.ref_old} }} }}", - "variable_ids": [(6, 0, [cls.variable_version.id])], - } - ) - - cls.file = cls.File.create( - { - "name": "test_version.txt", - "server_dir": "/tmp", - "code": f"{{ {{ {cls.ref_old} }} }}", - "variable_ids": [(6, 0, [cls.variable_version.id])], - } - ) - - def _rename(self): - """Rename variable and invalidate caches for records under test.""" - self.variable_version.write({"reference": self.ref_new}) - self.command.invalidate_recordset() - self.file.invalidate_recordset() - - def test_false_references_are_ignored(self): - """Ignore malformed or non-Jinja references.""" - cmd_plain = self.Command.create( - { - "name": "Plain", - "code": "print(test_version)", - "variable_ids": [(6, 0, [self.variable_version.id])], - } - ) - cmd_bad = self.Command.create( - { - "name": "BadBrackets", - "code": "{test_version}", - "variable_ids": [(6, 0, [self.variable_version.id])], - } - ) - - self._rename() - cmd_plain.invalidate_recordset() - cmd_bad.invalidate_recordset() - - self.assertEqual(cmd_plain.code, "print(test_version)") - self.assertEqual(cmd_bad.code, "{test_version}") - - def test_multiple_occurrences_replace_all(self): - """Replace all valid Jinja references in one field.""" - code = "A: {{ test_version }}, B: {{ test_version }}, C-end" - cmd_multi = self.Command.create( - { - "name": "Multi", - "code": code, - "variable_ids": [(6, 0, [self.variable_version.id])], - } - ) - - self._rename() - cmd_multi.invalidate_recordset() - actual_ref = self.variable_version.reference - expected = f"A: {{{{ {actual_ref} }}}}, " f"B: {{{{ {actual_ref} }}}}, C-end" - self.assertEqual(cmd_multi.code, expected) - - def test_template_files_updated(self): - """Propagate rename in template and generated file.""" - tpl = self.env["cx.tower.file.template"].create( - { - "name": "TmpTpl", - "file_name": "tpl.txt", - "server_dir": "/tmp", - "code": "{{ test_version }}", - "variable_ids": [(6, 0, [self.variable_version.id])], - } - ) - tpl_file = self.File.create( - { - "name": "from_tpl.txt", - "server_dir": "/tmp", - "template_id": tpl.id, - "code": "{{ test_version }}", - } - ) - - self._rename() - tpl.invalidate_recordset() - tpl_file.invalidate_recordset() - - actual_ref = self.variable_version.reference - expected = f"{{{{ {actual_ref} }}}}" - self.assertEqual(tpl.code, expected) - self.assertEqual(tpl_file.code, expected) - - def test_value_and_plan_line_update(self): - """Update value_char and plan line condition.""" - - def patched_mapping(_): - return { - "cx.tower.command": ["code", "path"], - "cx.tower.file": ["code", "server_dir", "name"], - "cx.tower.file.template": ["code", "server_dir", "file_name"], - "cx.tower.variable.value": ["value_char"], - "cx.tower.plan.line": ["condition"], - } - - with patch.object( - type(self.variable_version), - "_get_propagation_field_mapping", - patched_mapping, - ): - val = self.env["cx.tower.variable.value"].create( - { - "variable_id": self.variable_version.id, - "value_char": "hello {{ test_version }} world", - } - ) - - pl = self.plan_line_1 - pl.write( - { - "variable_ids": [(6, 0, [self.variable_version.id])], - "condition": "if {{ test_version }} then", - } - ) - - self.assertIn(self.variable_version.id, pl.variable_ids.ids) - - self._rename() - val.invalidate_recordset() - pl.invalidate_recordset() - - actual_ref = self.variable_version.reference - expected_val = f"hello {{{{ {actual_ref} }}}} world" - self.assertEqual(val.value_char, expected_val) - expected_cond = f"if {{{{ {actual_ref} }}}} then" - self.assertEqual(pl.condition, expected_cond) - - def test_variable_reference_update(self): - """Test variable reference update cascades to dependent models""" - # 1. Add a variable value to variable_os - variable_value = self.VariableValue.create( - { - "variable_id": self.variable_os.id, - "value_char": "Ubuntu 20.04", - "server_id": self.server_test_1.id, - } - ) - - # Store original references for comparison - original_variable_reference = self.variable_os.reference - original_variable_value_reference = variable_value.reference - - # 2. Change the reference for variable_os to "awesome_variable" - self.variable_os.write({"reference": "awesome_variable"}) - - # 3. Verify that references are updated for dependent models - # Invalidate models to refresh all references - self.env["cx.tower.variable"].invalidate_model(["reference"]) - self.env["cx.tower.variable.value"].invalidate_model(["reference"]) - - # Check that variable reference was updated - self.assertEqual(self.variable_os.reference, "awesome_variable") - self.assertNotEqual(self.variable_os.reference, original_variable_reference) - - # Check that variable value reference was updated - # to include the new variable reference - self.assertIn("awesome_variable", variable_value.reference) - self.assertNotEqual(variable_value.reference, original_variable_value_reference) - - # Verify the reference pattern for variable value follows the expected format: - # ___ # noqa: E501 - expected_variable_pattern = ( - f"{self.variable_os.reference}_variable_value_server_" - f"{self.server_test_1.reference}" - ) - self.assertEqual(variable_value.reference, expected_variable_pattern) diff --git a/addons/cetmix_tower_server/tests/test_variable_option.py b/addons/cetmix_tower_server/tests/test_variable_option.py deleted file mode 100644 index a833af6..0000000 --- a/addons/cetmix_tower_server/tests/test_variable_option.py +++ /dev/null @@ -1,285 +0,0 @@ -from odoo.exceptions import AccessError, ValidationError - -from .common import TestTowerCommon - - -class TestTowerVariableOption(TestTowerCommon): - """Test case class to validate the behavior of - 'cx.tower.variable.option' model. - """ - - @classmethod - def setUpClass(cls): - super().setUpClass() - - cls.variable_odoo_versions = cls.Variable.create( - { - "name": "odoo_versions", - "variable_type": "o", - } - ) - - cls.variable_option_17_0 = cls.VariableOption.create( - { - "name": "17.0", - "value_char": "17.0", - "variable_id": cls.variable_odoo_versions.id, - } - ) - - cls.variable_option_18_0 = cls.VariableOption.create( - { - "name": "18.0", - "value_char": "18.0", - "variable_id": cls.variable_odoo_versions.id, - } - ) - - # Create additional test users - cls.manager2 = cls.Users.create( - { - "name": "Manager 2", - "login": "manager2@example.com", - "groups_id": [(4, cls.group_manager.id)], - } - ) - - # Create variables with different access levels - cls.variable_level_1 = cls.Variable.create( - { - "name": "Level 1 Variable", - "access_level": "1", - } - ) - - cls.variable_level_2 = cls.Variable.create( - { - "name": "Level 2 Variable", - "access_level": "2", - } - ) - - # Create options with different access levels (inherited from variables) - cls.option_level_1 = cls.VariableOption.create( - { - "name": "Option Level 1", - "value_char": "value1", - "variable_id": cls.variable_level_1.id, - } - ) - - cls.option_level_2 = cls.VariableOption.create( - { - "name": "Option Level 2", - "value_char": "value2", - "variable_id": cls.variable_level_2.id, - } - ) - - def test_variable_value_set_from_option(self): - """Test that a variable value can be set from an option.""" - - variable_value = self.VariableValue.create( - { - "server_id": self.server_test_1.id, - "variable_id": self.variable_odoo_versions.id, - } - ) - - # -- 1 -- - # Set value_char to an existing option - variable_value.value_char = "17.0" - self.assertEqual( - variable_value.option_id, - self.variable_option_17_0, - ) - - # -- 2 -- - # Set value_char to a non-existing option - variable_meme_level = self.Variable.create( - { - "name": "meme_level", - "variable_type": "o", - } - ) - option_meme_level_high = self.VariableOption.create( - { - "name": "high", - "value_char": "high", - "variable_id": variable_meme_level.id, - } - ) - with self.assertRaises(ValidationError): - variable_value.option_id = option_meme_level_high - - # -- 3 -- - # Set value_char to a non-existing option - variable_value.value_char = "29.0" - self.assertFalse(variable_value.option_id) - - def test_access_level_consistency(self): - """Test that variable option access level cannot be lower - than variable access level.""" - - # Create a variable with access level "2" - variable_restricted = self.Variable.create( - { - "name": "restricted_variable", - "variable_type": "o", - "access_level": "2", - } - ) - - # Should succeed: option with same access level as variable - try: - self.VariableOption.create( - { - "name": "Option 1", - "value_char": "value1", - "variable_id": variable_restricted.id, - "access_level": "2", - } - ) - except ValidationError: - self.fail("Should allow creating option with same access level as variable") - - # Should succeed: option with higher access level than variable - try: - self.VariableOption.create( - { - "name": "Option 2", - "value_char": "value2", - "variable_id": variable_restricted.id, - "access_level": "3", - } - ) - except ValidationError: - self.fail( - "Should allow creating option with higher access level than variable" - ) - - # Should fail: option with lower access level than variable - with self.assertRaises( - ValidationError, - msg="Should not allow creating option " - "with lower access level than variable", - ): - self.VariableOption.create( - { - "name": "Option 3", - "value_char": "value3", - "variable_id": variable_restricted.id, - "access_level": "1", - } - ) - - # Test updating existing option's access level - option = self.VariableOption.create( - { - "name": "Option 4", - "value_char": "value4", - "variable_id": variable_restricted.id, - "access_level": "2", - } - ) - - # Should fail: updating to lower access level than variable - with self.assertRaises( - ValidationError, - msg="Should not allow updating option to lower access level than variable", - ): - option.write({"access_level": "1"}) - - # Should succeed: updating to higher access level than variable - try: - option.write({"access_level": "3"}) - except ValidationError: - self.fail( - "Should allow updating option to higher access level than variable" - ) - - def test_variable_option_access_rights(self): - """ - Test access rights for variable options - based on access levels and user roles. - """ - - # Test User Access - # --------------- - # Should see level 1 options only - records = self.VariableOption.with_user(self.user).search( - [("id", "in", [self.option_level_1.id, self.option_level_2.id])] - ) - self.assertEqual(len(records), 1, "User should only see level 1 options") - self.assertEqual( - records.id, self.option_level_1.id, "User should only see level 1 options" - ) - - # Test Manager Access - # ----------------- - # Should see level 1 and 2 options - records = self.VariableOption.with_user(self.manager).search( - [("id", "in", [self.option_level_1.id, self.option_level_2.id])] - ) - self.assertEqual(len(records), 2, "Manager should see level 1 and 2 options") - self.assertIn( - self.option_level_1.id, records.ids, "Manager should see level 1 options" - ) - self.assertIn( - self.option_level_2.id, records.ids, "Manager should see level 2 options" - ) - - # Test Manager Write Access - # ----------------------- - # Create an option as manager - manager_option = self.VariableOption.with_user(self.manager).create( - { - "name": "Manager Created Option", - "value_char": "manager_value", - "variable_id": self.variable_level_2.id, - } - ) - - # Manager should be able to modify their own option - try: - manager_option.with_user(self.manager).write({"name": "Updated Name"}) - except AccessError: - self.fail("Manager should be able to modify their own options") - - # Manager should not be able to modify another manager's option - manager2_option = self.VariableOption.with_user(self.manager2).create( - { - "name": "Other Manager Option", - "value_char": "other_value", - "variable_id": self.variable_level_2.id, - } - ) - - with self.assertRaises(AccessError): - manager2_option.with_user(self.manager).write({"name": "Try Update"}) - - # Test Root Access - # -------------- - # Root should see all options - records = self.VariableOption.with_user(self.root).search( - [("id", "in", [self.option_level_1.id, self.option_level_2.id])] - ) - self.assertEqual(len(records), 2, "Root should see all options") - - # Root should be able to create any option - try: - self.VariableOption.with_user(self.root).create( - { - "name": "Root Created Option", - "value_char": "root_value", - "variable_id": self.variable_level_2.id, - } - ) - except AccessError: - self.fail("Root should be able to create any option") - - # Root should be able to modify any option - try: - self.option_level_2.with_user(self.root).write({"name": "Updated by Root"}) - except AccessError: - self.fail("Root should be able to modify any option") diff --git a/addons/cetmix_tower_server/tests/test_variable_value.py b/addons/cetmix_tower_server/tests/test_variable_value.py deleted file mode 100644 index 7d4b38e..0000000 --- a/addons/cetmix_tower_server/tests/test_variable_value.py +++ /dev/null @@ -1,564 +0,0 @@ -from odoo.exceptions import AccessError - -from . import common - - -class TestTowerVariableValue(common.TestTowerCommon): - """Testing variable values.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - - # Create additional test users - cls.user2 = cls.Users.create( - { - "name": "Test User 2", - "login": "test_user2", - "email": "test_user2@example.com", - "groups_id": [(6, 0, [cls.group_user.id])], - } - ) - - cls.manager2 = cls.Users.create( - { - "name": "Test Manager 2", - "login": "test_manager2", - "email": "test_manager2@example.com", - "groups_id": [(6, 0, [cls.group_manager.id])], - } - ) - - # Create variables with different access levels - cls.variable_level_1 = cls.Variable.create( - { - "name": "Level 1 Variable", - "access_level": "1", - } - ) - - cls.variable_level_2 = cls.Variable.create( - { - "name": "Level 2 Variable", - "access_level": "2", - } - ) - - # Create servers - cls.server_1 = cls.Server.create( - { - "name": "Test Server 1", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "os_id": cls.os_debian_10.id, - "user_ids": [(4, cls.user.id)], - "manager_ids": [(4, cls.manager.id)], - } - ) - - cls.server_2 = cls.Server.create( - { - "name": "Test Server 2", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password", - "os_id": cls.os_debian_10.id, - "user_ids": [(4, cls.user2.id)], - "manager_ids": [(4, cls.manager2.id)], - } - ) - - # Create test command - cls.test_command = cls.Command.create( - { - "name": "Test Command", - "code": "echo 'test'", - } - ) - - # Create flight plan and its components - cls.test_plan = cls.Plan.create( - { - "name": "Test Plan", - "user_ids": [(4, cls.user.id)], - "manager_ids": [(4, cls.manager.id)], - } - ) - - cls.test_plan_line = cls.plan_line.create( - { - "name": "Test Line", - "plan_id": cls.test_plan.id, - "command_id": cls.test_command.id, - } - ) - - cls.test_plan_line_action = cls.plan_line_action.create( - { - "name": "Test Action", - "line_id": cls.test_plan_line.id, - "condition": "==", - "value_char": "0", - "action": "n", - } - ) - - # Create variable values - cls.global_value_1 = cls.VariableValue.create( - { - "variable_id": cls.variable_level_1.id, - "value_char": "global_value_1", - } - ) - - cls.global_value_2 = cls.VariableValue.create( - { - "variable_id": cls.variable_level_2.id, - "value_char": "global_value_2", - } - ) - - cls.server_value_1 = cls.VariableValue.create( - { - "variable_id": cls.variable_level_1.id, - "value_char": "server_value_1", - "server_id": cls.server_1.id, - } - ) - - cls.server_value_2 = cls.VariableValue.with_user(cls.manager).create( - { - "variable_id": cls.variable_level_2.id, - "value_char": "server_value_2", - "server_id": cls.server_1.id, - } - ) - - cls.plan_value_1 = cls.VariableValue.create( - { - "variable_id": cls.variable_level_1.id, - "value_char": "plan_value_1", - "plan_line_action_id": cls.test_plan_line_action.id, - } - ) - - cls.plan_value_2 = cls.VariableValue.create( - { - "variable_id": cls.variable_level_2.id, - "value_char": "plan_value_2", - "plan_line_action_id": cls.test_plan_line_action.id, - } - ) - - # Add server template setup - cls.server_template = cls.ServerTemplate.create( - { - "name": "Test Template", - "ssh_username": "admin", - "ssh_password": "password", - "os_id": cls.os_debian_10.id, - "manager_ids": [ - (4, cls.manager.id) - ], # Only managers should have access - } - ) - - # Add template variable values - cls.template_value_1 = cls.VariableValue.create( - { - "variable_id": cls.variable_level_1.id, - "value_char": "template_value_1", - "server_template_id": cls.server_template.id, - } - ) - - cls.template_value_2 = cls.VariableValue.with_user(cls.manager).create( - { - "variable_id": cls.variable_level_2.id, - "value_char": "template_value_2", - "server_template_id": cls.server_template.id, - } - ) - - # Add server to plan - cls.test_plan.write({"server_ids": [(4, cls.server_1.id)]}) - - def test_variable_value_access_rights(self): - """ - Test access rights for variable values - based on access levels and user roles. - """ - - # Test User Access - # --------------- - user_values = self.VariableValue.with_user(self.user).search( - [ - ( - "id", - "in", - [ - self.global_value_1.id, - self.global_value_2.id, - self.server_value_1.id, - self.server_value_2.id, - self.plan_value_1.id, - self.plan_value_2.id, - ], - ) - ] - ) - - # User should see level 1 global values and level 1 values - # from their server/plan - self.assertEqual(len(user_values), 3) - self.assertIn(self.global_value_1.id, user_values.ids) - self.assertIn(self.server_value_1.id, user_values.ids) - self.assertIn(self.plan_value_1.id, user_values.ids) - - # User should not be able to create/write/unlink values - with self.assertRaises(AccessError): - self.VariableValue.with_user(self.user).create( - { - "variable_id": self.variable_level_1.id, - "value_char": "test", - "server_id": self.server_1.id, - } - ) - - with self.assertRaises(AccessError): - self.server_value_1.with_user(self.user).write({"value_char": "new_value"}) - - with self.assertRaises(AccessError): - self.server_value_1.with_user(self.user).unlink() - - # Test Manager Access - # ------------------ - manager_values = self.VariableValue.with_user(self.manager).search( - [ - ( - "id", - "in", - [ - self.global_value_1.id, - self.global_value_2.id, - self.server_value_1.id, - self.server_value_2.id, - self.plan_value_1.id, - self.plan_value_2.id, - ], - ) - ] - ) - - # Manager should see all level 1 and 2 values from their server/plan - self.assertEqual(len(manager_values), 6) - - # Manager should be able to create values for their server/plan - test_variable = self.Variable.create( - { - "name": "Test Variable", - "access_level": "2", - } - ) - try: - new_value = self.VariableValue.with_user(self.manager).create( - { - "variable_id": test_variable.id, - "value_char": "manager_value", - "server_id": self.server_1.id, - } - ) - except AccessError: - self.fail("Manager should be able to create values for their server") - - # Manager should be able to modify values for their server/plan - try: - self.server_value_2.with_user(self.manager).write( - {"value_char": "updated_value"} - ) - except AccessError: - self.fail("Manager should be able to modify values for their server") - - # Manager should be able to delete their own values - try: - new_value.with_user(self.manager).unlink() - except AccessError: - self.fail("Manager should be able to delete their own values") - - # Manager should not be able to modify other manager's values - with self.assertRaises(AccessError): - self.VariableValue.with_user(self.manager).create( - { - "variable_id": self.variable_level_1.id, - "value_char": "test", - "server_id": self.server_2.id, - } - ) - - # Test Root Access - # --------------- - root_values = self.VariableValue.with_user(self.root).search( - [ - ( - "id", - "in", - [ - self.global_value_1.id, - self.global_value_2.id, - self.server_value_1.id, - self.server_value_2.id, - self.plan_value_1.id, - self.plan_value_2.id, - ], - ) - ] - ) - - # Root should see all values - self.assertEqual(len(root_values), 6) - - # Root should be able to create any value - try: - root_value = self.VariableValue.with_user(self.root).create( - { - "variable_id": self.variable_level_2.id, - "value_char": "root_value", - "server_id": self.server_2.id, - "access_level": "2", - } - ) - except AccessError: - self.fail("Root should be able to create any value") - - # Root should be able to modify any value - try: - self.server_value_2.with_user(self.root).write( - {"value_char": "root_updated"} - ) - except AccessError: - self.fail("Root should be able to modify any value") - - # Root should be able to delete any value - try: - root_value.with_user(self.root).unlink() - except AccessError: - self.fail("Root should be able to delete any value") - - def test_server_template_access(self): - """Test access rights for server template variable values""" - - # Test user access to template values - # (should see none since they don't have template access) - user_template_values = self.VariableValue.with_user(self.user).search( - [("server_template_id", "=", self.server_template.id)] - ) - self.assertEqual( - len(user_template_values), 0 - ) # Users can't see template values - - # Test manager access to template values - manager_template_values = self.VariableValue.with_user(self.manager).search( - [("server_template_id", "=", self.server_template.id)] - ) - self.assertEqual(len(manager_template_values), 2) - - # Create a new variable for testing manager create rights - test_variable = self.Variable.create( - { - "name": "Test Template Manager Variable", - "access_level": "2", - } - ) - - # Test manager create rights - new_template_value = self.VariableValue.with_user(self.manager).create( - { - "variable_id": test_variable.id, # Use the new variable - "value_char": "new_template_value", - "server_template_id": self.server_template.id, - } - ) - self.assertTrue(new_template_value.exists()) - - # Test manager write rights - self.template_value_2.with_user(self.manager).write( - {"value_char": "updated_template_value"} - ) - self.assertEqual(self.template_value_2.value_char, "updated_template_value") - - # Test manager unlink rights (only own records) - new_template_value.with_user(self.manager).unlink() - self.assertFalse(new_template_value.exists()) - - def test_server_template_manager_in_users_access(self): - """Test access rights for server template when manager is in user_ids only""" - - # Create new template with manager in user_ids only (not in manager_ids) - template_with_manager_user = self.ServerTemplate.create( - { - "name": "Template With Manager User", - "ssh_username": "admin", - "ssh_password": "password", - "os_id": self.os_debian_10.id, - "user_ids": [(4, self.manager.id)], # Add manager to user_ids only - } - ) - - # Create test value as root to set up the test - template_value_1 = self.VariableValue.create( - { - "variable_id": self.variable_level_1.id, - "value_char": "manager_user_value_1", - "server_template_id": template_with_manager_user.id, - } - ) - - # Test manager can only read level 1 values (like a regular user) - manager_values = self.VariableValue.with_user(self.manager).search( - [("server_template_id", "=", template_with_manager_user.id)] - ) - self.assertEqual(len(manager_values), 1) - self.assertIn(template_value_1.id, manager_values.ids) - - # Create a new variable for testing create access - test_variable = self.Variable.create( - { - "name": "Test Template User Variable", - "access_level": "1", - } - ) - - # Test manager cannot create values - with self.assertRaises(AccessError): - self.VariableValue.with_user(self.manager).create( - { - "variable_id": test_variable.id, # Use the new variable - "value_char": "new_manager_user_value", - "server_template_id": template_with_manager_user.id, - } - ) - - # Test manager cannot write values - with self.assertRaises(AccessError): - template_value_1.with_user(self.manager).write( - {"value_char": "updated_manager_user_value"} - ) - - # Test manager cannot delete values - with self.assertRaises(AccessError): - template_value_1.with_user(self.manager).unlink() - - def test_plan_server_access(self): - """Test access rights for plan server variable values""" - - # Create a new variable for testing - test_variable = self.Variable.create( - { - "name": "Test Plan Server Variable", - "access_level": "2", - } - ) - - # Create variable value for plan server (only assign to server) - plan_server_value = self.VariableValue.with_user(self.manager).create( - { - "variable_id": test_variable.id, - "value_char": "plan_server_value", - "server_id": self.server_1.id, - } - ) - - # Test user read access - user_plan_server_values = self.VariableValue.with_user(self.user).search( - [("server_id", "=", self.server_1.id), ("access_level", "=", "1")] - ) - self.assertTrue(user_plan_server_values) - - # Test manager read/write access - manager_plan_server_values = self.VariableValue.with_user(self.manager).search( - [("server_id", "=", self.server_1.id)] - ) - self.assertTrue(manager_plan_server_values) - - # Test manager write rights - plan_server_value.with_user(self.manager).write( - {"value_char": "updated_plan_server_value"} - ) - self.assertEqual(plan_server_value.value_char, "updated_plan_server_value") - - # Create another new variable for testing create rights - test_variable_2 = self.Variable.create( - { - "name": "Test Plan Server Variable 2", - "access_level": "2", - } - ) - - # Test manager create rights (only assign to server) - new_plan_server_value = self.VariableValue.with_user(self.manager).create( - { - "variable_id": test_variable_2.id, - "value_char": "new_plan_server_value", - "server_id": self.server_1.id, - } - ) - self.assertTrue(new_plan_server_value.exists()) - - # Test manager unlink rights (only own records) - new_plan_server_value.with_user(self.manager).unlink() - self.assertFalse(new_plan_server_value.exists()) - - # Test plan-specific variable values - test_variable_3 = self.Variable.create( - { - "name": "Test Plan Action Variable", - "access_level": "2", - } - ) - - # Create variable value for plan action - plan_action_value = self.VariableValue.with_user(self.manager).create( - { - "variable_id": test_variable_3.id, - "value_char": "plan_action_value", - "plan_line_action_id": self.test_plan_line_action.id, - } - ) - self.assertTrue(plan_action_value.exists()) - - # Test manager access to plan action values - manager_plan_values = self.VariableValue.with_user(self.manager).search( - [("plan_line_action_id", "=", self.test_plan_line_action.id)] - ) - self.assertIn(plan_action_value.id, manager_plan_values.ids) - - def test_reference_pattern_global_server_template_action(self): - """Ensure model-scoped references follow the required pattern.""" - # Global - model_ref = self.VariableValue._get_model_generic_reference() - self.assertTrue(self.global_value_1.reference.endswith(f"_{model_ref}_global")) - - # Server - srv_model_ref = self.Server._get_model_generic_reference() - self.assertTrue( - self.server_value_1.reference.startswith( - f"{self.variable_level_1.reference}_{model_ref}_{srv_model_ref}_" - ) - ) - - # Server Template - tmpl_model_ref = self.ServerTemplate._get_model_generic_reference() - self.assertTrue( - self.template_value_1.reference.startswith( - f"{self.variable_level_1.reference}_{model_ref}_{tmpl_model_ref}_" - ) - ) - - # Plan Line Action - action_model_ref = self.plan_line_action._get_model_generic_reference() - self.assertTrue( - self.plan_value_1.reference.startswith( - f"{self.variable_level_1.reference}_{model_ref}_{action_model_ref}_" - ) - ) diff --git a/addons/cetmix_tower_server/tests/test_vault_mixin.py b/addons/cetmix_tower_server/tests/test_vault_mixin.py deleted file mode 100644 index 6f2e6af..0000000 --- a/addons/cetmix_tower_server/tests/test_vault_mixin.py +++ /dev/null @@ -1,506 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - - -from .common import TestTowerCommon - - -class TestVaultMixin(TestTowerCommon): - """Test vault mixin functionality.""" - - def test_vault_mixin_secret_fields(self): - """Test vault mixin functionality for secret fields - (host_key and ssh_password)""" - # Create a server with initial secret values - initial_password = "initial_password" - initial_host_key = "initial_host_key" - - server = self.Server.create( - { - "name": "Vault Test Server", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": initial_password, - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "host_key": initial_host_key, - "skip_host_key": False, - } - ) - - # Test 1: Verify initial values are stored in vault and accessible - # Read values using common way - should return placeholder - self.assertEqual( - server.ssh_password, - self.Server.SECRET_VALUE_PLACEHOLDER, - "ssh_password should return placeholder value when read normally", - ) - self.assertEqual( - server.host_key, - self.Server.SECRET_VALUE_PLACEHOLDER, - "host_key should return placeholder value when read normally", - ) - - # Read using _get_secret_values() - should return actual initial values - secret_values = server._get_secret_values() - self.assertIsNotNone(secret_values, "secret_values should not be None") - self.assertIn(server.id, secret_values, "Server ID should be in secret values") - - server_secrets = secret_values[server.id] - self.assertIn( - "ssh_password", server_secrets, "ssh_password should be in secret values" - ) - self.assertIn("host_key", server_secrets, "host_key should be in secret values") - - self.assertEqual( - server_secrets["ssh_password"], - initial_password, - "ssh_password should return initial value from vault", - ) - self.assertEqual( - server_secrets["host_key"], - initial_host_key, - "host_key should return initial value from vault", - ) - - # Read individual fields using _get_secret_value() - # should return initial values - retrieved_password = server._get_secret_value("ssh_password") - retrieved_host_key = server._get_secret_value("host_key") - - self.assertEqual( - retrieved_password, - initial_password, - "_get_secret_value should return correct initial ssh_password", - ) - self.assertEqual( - retrieved_host_key, - initial_host_key, - "_get_secret_value should return correct initial host_key", - ) - - # Test 2: Save new values to secret fields - new_password = "new_secure_password_123" - new_host_key = "new_host_key_456" - - server.write( - { - "ssh_password": new_password, - "host_key": new_host_key, - } - ) - - # Test 3: Read values using common way after update - should return placeholder - # Note: In Odoo, we need to re-read the record to see updated values - server = self.Server.browse(server.id) - self.assertEqual( - server.ssh_password, - self.Server.SECRET_VALUE_PLACEHOLDER, - "ssh_password should return placeholder value when read normally " - "after update", - ) - self.assertEqual( - server.host_key, - self.Server.SECRET_VALUE_PLACEHOLDER, - "host_key should return placeholder value when read normally " - "after update", - ) - - # Test 4: Read using _get_secret_values() after update - # should return new values - secret_values = server._get_secret_values() - self.assertIsNotNone( - secret_values, "secret_values should not be None after update" - ) - self.assertIn( - server.id, - secret_values, - "Server ID should be in secret values after update", - ) - - server_secrets = secret_values[server.id] - self.assertIn( - "ssh_password", - server_secrets, - "ssh_password should be in secret values after update", - ) - self.assertIn( - "host_key", - server_secrets, - "host_key should be in secret values after update", - ) - - self.assertEqual( - server_secrets["ssh_password"], - new_password, - "ssh_password should return new value from vault after update", - ) - self.assertEqual( - server_secrets["host_key"], - new_host_key, - "host_key should return new value from vault after update", - ) - - # Test 5: Read individual fields using _get_secret_value() after update - # Get both values in one call using _get_secret_values() - secret_values = server._get_secret_values() - self.assertIsNotNone( - secret_values, "secret_values should not be None for individual field test" - ) - self.assertIn( - server.id, - secret_values, - "Server ID should be in secret values for individual field test", - ) - - server_secrets = secret_values[server.id] - retrieved_password = server_secrets["ssh_password"] - retrieved_host_key = server_secrets["host_key"] - - self.assertEqual( - retrieved_password, - new_password, - "_get_secret_values should return correct new ssh_password after update", - ) - self.assertEqual( - retrieved_host_key, - new_host_key, - "_get_secret_values should return correct new host_key after update", - ) - - # Test 6: Verify that non-secret fields are not affected - self.assertEqual( - server.name, - "Vault Test Server", - "Non-secret field should not be affected by vault mixin", - ) - self.assertEqual( - server.ssh_username, - "admin", - "Non-secret field should not be affected by vault mixin", - ) - - def test_vault_mixin_create_with_secret_fields(self): - """Test vault mixin functionality when creating records with secret fields""" - # Create a server with secret fields - server = self.Server.create( - { - "name": "Create Test Server", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "create_password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "host_key": "create_host_key", - "skip_host_key": False, - } - ) - - # Verify secret fields are stored in vault and not in main table - self.assertEqual( - server.ssh_password, - self.Server.SECRET_VALUE_PLACEHOLDER, - "ssh_password should return placeholder after creation", - ) - self.assertEqual( - server.host_key, - self.Server.SECRET_VALUE_PLACEHOLDER, - "host_key should return placeholder after creation", - ) - - # Verify actual values are accessible via vault methods - secret_values = server._get_secret_values() - self.assertIn( - server.id, - secret_values, - "Server ID should be in secret values after creation", - ) - - server_secrets = secret_values[server.id] - self.assertEqual( - server_secrets["ssh_password"], - "create_password", - "ssh_password should be stored in vault after creation", - ) - self.assertEqual( - server_secrets["host_key"], - "create_host_key", - "host_key should be stored in vault after creation", - ) - - def test_vault_mixin_delete_secret_fields(self): - """Test vault mixin functionality when deleting secret field values""" - # Create a server with secret fields - server = self.Server.create( - { - "name": "Delete Test Server", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "delete_password", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "host_key": "delete_host_key", - "skip_host_key": False, - } - ) - - # Verify initial values exist - secret_values = server._get_secret_values() - self.assertIn( - "ssh_password", - secret_values[server.id], - "ssh_password should exist initially", - ) - self.assertIn( - "host_key", secret_values[server.id], "host_key should exist initially" - ) - - # Delete secret field values - server.write( - { - "ssh_password": False, - "host_key": False, - } - ) - - # Verify values are removed from vault - secret_values = server._get_secret_values() - server_secrets = secret_values.get(server.id, {}) - - self.assertNotIn( - "ssh_password", server_secrets, "ssh_password should be removed from vault" - ) - self.assertNotIn( - "host_key", server_secrets, "host_key should be removed from vault" - ) - - # Verify normal field access still returns placeholders - server = self.Server.browse(server.id) - self.assertEqual( - server.ssh_password, - self.Server.SECRET_VALUE_PLACEHOLDER, - "ssh_password should return placeholder after deletion", - ) - self.assertEqual( - server.host_key, - self.Server.SECRET_VALUE_PLACEHOLDER, - "host_key should return placeholder after deletion", - ) - - def test_vault_mixin_bulk_create_with_secret_fields(self): - """Test vault mixin functionality when creating multiple servers with different - secret field configurations""" - placeholder = self.Server.SECRET_VALUE_PLACEHOLDER - # Create 3 servers with different secret field configurations - servers_data = [ - { - "name": "Server 1 - Both Fields", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password1", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "host_key": "host_key1", - "skip_host_key": False, - }, - { - "name": "Server 2 - Host Key Only", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_auth_mode": "k", - "os_id": self.os_debian_10.id, - "host_key": "host_key2", - "skip_host_key": False, - "ssh_key_id": self.key_1.id, - }, - { - "name": "Server 3 - SSH Password Only", - "ip_v4_address": "localhost", - "ssh_username": "admin", - "ssh_password": "password3", - "ssh_auth_mode": "p", - "os_id": self.os_debian_10.id, - "skip_host_key": True, - }, - ] - - # Create all servers in one call - servers = self.Server.create(servers_data) - - # Verify we have 3 servers - self.assertEqual(len(servers), 3, "Should have created 3 servers") - - # Test 1: Get values for all 3 servers regular way - should return placeholders - for server in servers: - self.assertEqual( - server.ssh_password, - placeholder, - f"Server {server.name} ssh_password should return placeholder " - f"when read normally", - ) - - self.assertEqual( - server.host_key, - placeholder, - f"Server {server.name} host_key should return placeholder " - f"when read normally", - ) - - # Test 2: Get values for all 3 servers at once using _get_secret_values() - all_secret_values = servers._get_secret_values() - self.assertIsNotNone(all_secret_values, "all_secret_values should not be None") - - # Verify Server 1 (both fields) - server1 = servers[0] - self.assertIn( - server1.id, all_secret_values, "Server 1 should be in secret values" - ) - server1_secrets = all_secret_values[server1.id] - - self.assertEqual( - server1_secrets.get("ssh_password"), - "password1", - "Server 1 ssh_password should be preserved correctly in vault", - ) - self.assertEqual( - server1_secrets.get("host_key"), - "host_key1", - "Server 1 host_key should be preserved correctly in vault", - ) - - # Verify Server 2 (host key only) - server2 = servers[1] - self.assertIn( - server2.id, all_secret_values, "Server 2 should be in secret values" - ) - server2_secrets = all_secret_values[server2.id] - - self.assertIsNone( - server2_secrets.get("ssh_password"), - "Server 2 should not have ssh_password in vault", - ) - self.assertEqual( - server2_secrets.get("host_key"), - "host_key2", - "Server 2 host_key should be preserved correctly in vault", - ) - - # Verify Server 3 (ssh password only) - server3 = servers[2] - self.assertIn( - server3.id, all_secret_values, "Server 3 should be in secret values" - ) - server3_secrets = all_secret_values[server3.id] - - self.assertEqual( - server3_secrets.get("ssh_password"), - "password3", - "Server 3 ssh_password should be preserved correctly in vault", - ) - self.assertIsNone( - server3_secrets.get("host_key"), - "Server 3 should not have host_key in vault", - ) - - # Test 3: Verify that non-secret fields are not affected - for server in servers: - self.assertIsNotNone( - server.name, - f"Server {server.id} name should not be affected by vault mixin", - ) - self.assertIsNotNone( - server.ssh_username, - f"Server {server.id} ssh_username should not be affected " - f"by vault mixin", - ) - self.assertIsNotNone( - server.ip_v4_address, - f"Server {server.id} ip_v4_address should not be affected " - f"by vault mixin", - ) - - # Test 4: Modify secret fields and verify changes are handled correctly - # Change the ssh password and remove the host key from Server 1 - server1 = servers.filtered(lambda s: s.name == "Server 1 - Both Fields") - server1.write( - { - "ssh_password": "updated_password1", - "host_key": False, - } - ) - - # Remove host key and add an ssh password in Server 2 - server2 = servers.filtered(lambda s: s.name == "Server 2 - Host Key Only") - server2.write( - { - "host_key": False, - "ssh_password": "new_password2", - } - ) - - # Remove ssh password from Server 3 - server3 = servers.filtered(lambda s: s.name == "Server 3 - SSH Password Only") - server3.write( - { - "ssh_password": False, - } - ) - - # Test 5: Get values for all 3 servers regular way after modifications - # Ensure that all values are replaced with placeholders - for server in servers: - self.assertEqual( - server.ssh_password, - placeholder, - f"Server {server.id} ssh_password should return placeholder " - f"after modifications", - ) - self.assertEqual( - server.host_key, - placeholder, - f"Server {server.id} host_key should return placeholder " - f"after modifications", - ) - - # Test 6: Get values for all 3 servers at once using _get_secret_values() - # Ensure that all values are preserved correctly after modifications - all_secret_values = servers._get_secret_values() - self.assertIsNotNone( - all_secret_values, - "all_secret_values should not be None after modifications", - ) - - # Verify Server 1 (updated password, no host key) - server1 = servers[0] - server1_secrets = all_secret_values[server1.id] - - self.assertEqual( - server1_secrets.get("ssh_password"), - "updated_password1", - "Server 1 ssh_password should be updated correctly in vault", - ) - self.assertIsNone( - server1_secrets.get("host_key"), - "Server 1 host_key should be removed from vault", - ) - - # Verify Server 2 (new password, no host key) - server2_secrets = all_secret_values[server2.id] - - self.assertEqual( - server2_secrets.get("ssh_password"), - "new_password2", - "Server 2 ssh_password should be added correctly in vault", - ) - self.assertIsNone( - server2_secrets.get("host_key"), - "Server 2 host_key should be removed from vault", - ) - - # Verify Server 3 (no ssh password, no host key) - # Server 3 should not be in the result since it has no secret values - self.assertNotIn( - server3.id, - all_secret_values, - "Server 3 should not be in secret values since it has no secret fields", - ) diff --git a/addons/cetmix_tower_server/views/cx_tower_command_log_view.xml b/addons/cetmix_tower_server/views/cx_tower_command_log_view.xml deleted file mode 100644 index ab1c09e..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_command_log_view.xml +++ /dev/null @@ -1,216 +0,0 @@ - - - - cx.tower.command.log.view.form - cx.tower.command.log - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - cx.tower.command.log.view.tree - cx.tower.command.log - - - - - - - - - - - - - - - - cx.tower.command.log.view.search - cx.tower.command.log - - - - - - - - - - - - - - - - - - - - - - - - - - - Command Log - ir.actions.act_window - cx.tower.command.log - tree,form - {} - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_command_view.xml b/addons/cetmix_tower_server/views/cx_tower_command_view.xml deleted file mode 100644 index 02ff79c..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_command_view.xml +++ /dev/null @@ -1,330 +0,0 @@ - - - - - cx.tower.command.view.form - cx.tower.command - -
    - - -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - - - cx.tower.command.view.tree - cx.tower.command - - - - - - - - - - - - - - - - - - cx.tower.command.view.search - cx.tower.command - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Command - ir.actions.act_window - cx.tower.command - tree,form - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_file_template_view.xml b/addons/cetmix_tower_server/views/cx_tower_file_template_view.xml deleted file mode 100644 index 9c69b82..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_file_template_view.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - cx.tower.file.template.view.form - cx.tower.file.template - -
    - -
    - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - - - cx.tower.file.template.view.tree - cx.tower.file.template - - - - - - - - - - - - - - - cx.tower.file.template.view.search - cx.tower.file.template - - - - - - - - - - - - - - - - - - - Templates - cx.tower.file.template - tree,form - - -

    - Add a new file template -

    -
    -
    - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_file_view.xml b/addons/cetmix_tower_server/views/cx_tower_file_view.xml deleted file mode 100644 index 3e32a54..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_file_view.xml +++ /dev/null @@ -1,292 +0,0 @@ - - - - - cx.tower.file.view.form - cx.tower.file - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - -
    -
    -
    -
    - - - cx.tower.file.view.tree - cx.tower.file - - - - - - - - - - - - - - - - - - cx.tower.file.view.search - cx.tower.file - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Files - cx.tower.file - tree,form - - [] - {} - -

    - Add a new file -

    -
    -
    - - - Upload - - - code - action = records.action_push_to_server() - - - - Download - - - code - action = records.action_pull_from_server() - - - - Delete from server - - - code - action = records.action_delete_from_server() - - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_key_view.xml b/addons/cetmix_tower_server/views/cx_tower_key_view.xml deleted file mode 100644 index 2cada3e..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_key_view.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - cx.tower.key.view.form - cx.tower.key - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - -
    -
    -
    - - - - - - -
    -
    - -
    -
    - - - cx.tower.key.view.search - cx.tower.key - - - - - - - - - - - - - - - - - - cx.tower.key.view.tree - cx.tower.key - - - - - - - - - - SSH Key / Secret - ir.actions.act_window - cx.tower.key - tree,form - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_os_view.xml b/addons/cetmix_tower_server/views/cx_tower_os_view.xml deleted file mode 100644 index 5271427..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_os_view.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - cx.tower.os.view.form - cx.tower.os - -
    - - - - - - - - - - - - -
    -
    -
    - - - cx.tower.os.view.tree - cx.tower.os - - - - - - - - - - - - cx.tower.os.view.search - cx.tower.os - - - - - - - - - - - OS - ir.actions.act_window - cx.tower.os - tree,form - - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_plan_line_view.xml b/addons/cetmix_tower_server/views/cx_tower_plan_line_view.xml deleted file mode 100644 index 47e69c6..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_plan_line_view.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - - - cx.tower.plan.line.view.form - cx.tower.plan.line - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    AND

    - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -
    -
    - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_plan_line_view_action_view.xml b/addons/cetmix_tower_server/views/cx_tower_plan_line_view_action_view.xml deleted file mode 100644 index 1ccfaf8..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_plan_line_view_action_view.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - cx.tower.plan.line.view.action.form - cx.tower.plan.line.action - -
    - - - - -

    AND

    - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_plan_log_view.xml b/addons/cetmix_tower_server/views/cx_tower_plan_log_view.xml deleted file mode 100644 index 8dbdfbb..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_plan_log_view.xml +++ /dev/null @@ -1,217 +0,0 @@ - - - - cx.tower.plan.log.view.form - cx.tower.plan.log - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - cx.tower.plan.log.view.tree - cx.tower.plan.log - - - - - - - - - - - - - - - - cx.tower.plan.log.view.search - cx.tower.plan.log - - - - - - - - - - - - - - - - - - - - - - - - - - - Flight Plan Log - ir.actions.act_window - cx.tower.plan.log - tree,form - {'search_default_filter_main_plans': 1} - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_plan_view.xml b/addons/cetmix_tower_server/views/cx_tower_plan_view.xml deleted file mode 100644 index 76171a2..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_plan_view.xml +++ /dev/null @@ -1,212 +0,0 @@ - - - - - cx.tower.plan.view.form - cx.tower.plan - -
    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - - cx.tower.plan.view.tree - cx.tower.plan - - - - - - - - - - - - - cx.tower.plan.view.search - cx.tower.plan - - - - - - - - - - - - - - - - - - - - - - - - - Flight Plan - ir.actions.act_window - cx.tower.plan - tree,form - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_scheduled_task_view.xml b/addons/cetmix_tower_server/views/cx_tower_scheduled_task_view.xml deleted file mode 100644 index 6723fe1..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_scheduled_task_view.xml +++ /dev/null @@ -1,210 +0,0 @@ - - - - - cx.tower.scheduled.task.view.tree - cx.tower.scheduled.task - - - - - - - - - - - - - - - - cx.tower.scheduled.task.view.form - cx.tower.scheduled.task - -
    -
    -
    - - - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - - cx.tower.scheduled.task.search - cx.tower.scheduled.task - - - - - - - - - - - - - - - - - - - - - - - - Scheduled Tasks - ir.actions.act_window - cx.tower.scheduled.task - tree,form - - {'search_default_all': 1} - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_server_log_view.xml b/addons/cetmix_tower_server/views/cx_tower_server_log_view.xml deleted file mode 100644 index 3f7ff36..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_server_log_view.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - cx.tower.server.log.view.form - cx.tower.server.log - -
    -
    -
    - -
    -

    - -

    -

    - -

    -
    - - - - - - - - - - -
    -
    -
    -
    - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_server_template_view.xml b/addons/cetmix_tower_server/views/cx_tower_server_template_view.xml deleted file mode 100644 index 50e7448..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_server_template_view.xml +++ /dev/null @@ -1,386 +0,0 @@ - - - - - cx.tower.server.template.view.kanban - cx.tower.server.template - - - - - - - - - - -
    -
    -
    -
    - -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    - Servers: - - - -
    -
    - -
    - Operating System: - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - cx.tower.server.template.view.tree - cx.tower.server.template - - - - - - - - - - - - cx.tower.server.template.view.form - cx.tower.server.template - -
    -
    -
    - -
    - -
    - -
    - -

    - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -

    - -

    -
    - - - - - - - - -
    -
    -
    -
    - - - - - - - -
    - - - - -
    -
    -
    - - - - - - - -
    - - - - -
    -
    -
    -
    -
    -
    - - - -
    - -
    -
    - - - cx.tower.server.template.view.search - cx.tower.server.template - - - - - - - - - - - - - - - - - - - - - Server Templates - ir.actions.act_window - cx.tower.server.template - kanban,tree,form - - - -
    diff --git a/addons/cetmix_tower_server/views/cx_tower_server_view.xml b/addons/cetmix_tower_server/views/cx_tower_server_view.xml deleted file mode 100644 index 45f56b2..0000000 --- a/addons/cetmix_tower_server/views/cx_tower_server_view.xml +++ /dev/null @@ -1,558 +0,0 @@ - - - - - cx.tower.server.view.kanban - cx.tower.server - - - - - - - - - - - - -
    -
    -
    -
    - -
    - -
    -
    - -
    -
    - - - -
    -
    - - -
    -
    -
    -
    - Partner: - -
    -
    - Operating System: - -
    -
    - IPv4 Address: - -
    -
    - IPv6 Address: - -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - cx.tower.server.view.tree - cx.tower.server - - - - - - - - - - - - - - cx.tower.server.view.form - cx.tower.server - -
    -
    -
    - -
    - -
    - -
    - -

    - - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - -
    -
    -
    - - - - -
    -
    - -
    -
    - - - - cx.tower.variable.view.tree - cx.tower.variable - - - - - - - - - - - - - - cx.tower.variable.view.search - cx.tower.variable - - - - - - - - - - - - - - Variables - ir.actions.act_window - cx.tower.variable - tree,form - - -
    diff --git a/addons/cetmix_tower_server/views/menuitems.xml b/addons/cetmix_tower_server/views/menuitems.xml deleted file mode 100644 index 6cf85dd..0000000 --- a/addons/cetmix_tower_server/views/menuitems.xml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/cetmix_tower_server/views/res_config_settings.xml b/addons/cetmix_tower_server/views/res_config_settings.xml deleted file mode 100644 index 5feea4e..0000000 --- a/addons/cetmix_tower_server/views/res_config_settings.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - res.config.settings.view.form.inherit.cetmix.tower.settings - - res.config.settings - - - -
    -

    System Settings

    -
    -
    -
    -
    -
    -
    -
    -
    Pull files from server
    -
    - Files will be pulled from server to Tower - automatically using cron job. -
    -
    -
    -
    -
    -
    -
    Run scheduled tasks
    -
    - Scheduled tasks will be run automatically using cron job. -
    -
    -
    -
    -
    -

    Notifications

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - General Settings - ir.actions.act_window - res.config.settings - - form - inline - {'module' : 'cetmix_tower_server'} - -
    diff --git a/addons/cetmix_tower_server/views/res_partner_view.xml b/addons/cetmix_tower_server/views/res_partner_view.xml deleted file mode 100644 index 7b0d863..0000000 --- a/addons/cetmix_tower_server/views/res_partner_view.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - res.partner.form.inherit.cetmix.tower - res.partner - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - res.partner.select.inherit.cetmix.tower - res.partner - - - - - - - - - - - - - Partners - ir.actions.act_window - res.partner - {'search_default_filter_with_servers': 1} - kanban,tree,form,activity - - diff --git a/addons/cetmix_tower_server/wizards/__init__.py b/addons/cetmix_tower_server/wizards/__init__.py deleted file mode 100644 index c37257d..0000000 --- a/addons/cetmix_tower_server/wizards/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from . import cx_tower_command_run_wizard -from . import cx_tower_plan_run_wizard -from . import cx_tower_server_template_create_wizard -from . import cx_tower_server_host_key_wizard diff --git a/addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py deleted file mode 100644 index 6c010f3..0000000 --- a/addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py +++ /dev/null @@ -1,490 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from ansi2html import Ansi2HTMLConverter - -from odoo import _, api, fields, models -from odoo.exceptions import AccessError, ValidationError - -from ..models.tools import generate_random_id - -html_converter = Ansi2HTMLConverter(inline=True) - - -class CxTowerCommandRunWizard(models.TransientModel): - """ - Wizard to run a command on selected servers. - """ - - _name = "cx.tower.command.run.wizard" - _inherit = "cx.tower.template.mixin" - _description = "Run Command in Wizard" - - server_ids = fields.Many2many( - "cx.tower.server", - string="Servers", - ) - command_id = fields.Many2one( - "cx.tower.command", - ) - note = fields.Text(related="command_id.note", readonly=True) - action = fields.Selection( - selection=[ - ("ssh_command", "SSH command"), - ("python_code", "Python code"), - ], - default="ssh_command", - required=True, - ) - path = fields.Char( - compute="_compute_code", - readonly=False, - store=True, - help="Put custom path to run the command.\n" - "IMPORTANT: this field does NOT support variables!", - ) - command_domain = fields.Binary( - compute="_compute_command_domain", - ) - tag_ids = fields.Many2many( - comodel_name="cx.tower.tag", - string="Tags", - ) - use_sudo = fields.Boolean( - string="Use sudo", - help="Will use sudo based on server settings." - "If no sudo is configured will run without sudo", - ) - code = fields.Text(compute="_compute_code", readonly=False, store=True) - applicability = fields.Selection( - selection=[ - ("this", "For selected server(s)"), - ("shared", "Non server restricted"), - ], - default="shared", - required=True, - compute="_compute_show_servers", - readonly=False, - store=True, - help="Selected server(s): only Commands that are specific" - " to the selected server(s)\n" - "Non server restricted: all Commands that are " - "not specific to any server", - ) - rendered_code = fields.Text( - compute="_compute_rendered_code", - compute_sudo=True, - ) - result = fields.Html() - show_servers = fields.Boolean( - compute="_compute_show_servers", - compute_sudo=True, - ) - os_compatibility_warning = fields.Text( - compute="_compute_os_compatibility_warning", - compute_sudo=True, - help="Warning about OS compatibility of the command", - ) - command_variable_ids = fields.Many2many( - "cx.tower.variable", - related="command_id.variable_ids", - readonly=True, - string="Command Variables", - ) - custom_variable_value_ids = fields.One2many( - "cx.tower.command.run.wizard.variable.value", - "wizard_id", - ) - have_access_to_server = fields.Boolean( - compute="_compute_have_access_to_server", - ) - has_missing_required_values = fields.Boolean( - compute="_compute_has_missing_required_values" - ) - missing_required_variables_message = fields.Text( - compute="_compute_has_missing_required_values" - ) - - @api.model - def default_get(self, fields_list): - res = super().default_get(fields_list) - if not self._is_privileged_user(): - res["applicability"] = "this" - return res - - @api.depends("server_ids") - def _compute_show_servers(self): - for rec in self: - rec.show_servers = bool(rec.server_ids and len(rec.server_ids) > 1) - - @api.depends("command_id", "server_ids", "action") - def _compute_code(self): - """ - Set code after change command - """ - for record in self: - if record.command_id and record.server_ids: - # Render code preview for the first server only. - record.update( - { - "code": record.command_id.code, - "path": record.server_ids[0] - ._render_command(record.command_id) - .get("rendered_path"), - } - ) - else: - record.update({"code": False, "path": False}) - - @api.depends("code", "server_ids", "action", "custom_variable_value_ids.value_char") - def _compute_rendered_code(self): - for record in self: - if record.server_ids and len(record.server_ids) == 1: - # Render code preview for the first server only. - server_id = record.server_ids[0] - - # Get variable list - variables = record.get_variables() - - # Get variable values - variable_values = server_id.get_variable_values( - variables.get(str(record.id)) - ) - if variable_values and record.custom_variable_value_ids: - custom_vals = { - custom_value.variable_id.reference: custom_value.value_char - for custom_value in record.custom_variable_value_ids - if custom_value.variable_id - } - variable_values[server_id.id].update(custom_vals) - - # Render template - if variable_values: - record.rendered_code = record.render_code( - pythonic_mode=record.action == "python_code", - **variable_values.get(server_id.id), - ).get(self.id) # pylint: disable=no-member - else: - record.rendered_code = record.code - else: - record.rendered_code = record.code - - @api.depends("applicability", "server_ids", "tag_ids", "action") - def _compute_command_domain(self): - """ - Compose domain based on condition - """ - for record in self: - domain = [("action", "=", record.action)] - if record.applicability == "shared": - domain.append(("server_ids", "=", False)) - elif record.applicability == "this": - domain.append(("server_ids", "in", record.server_ids.ids)) - if record.tag_ids: - domain.append(("tag_ids", "in", record.tag_ids.ids)) - record.command_domain = domain - - @api.depends("command_id", "server_ids") - def _compute_os_compatibility_warning(self): - for wizard in self: - # Skip if command is not SSH command or no OS compatibility is defined - if ( - not wizard.command_id - or not wizard.server_ids - or wizard.command_id.action != "ssh_command" - or not wizard.command_id.os_ids - ): - wizard.os_compatibility_warning = False - continue - warning_list = [] - for server in wizard.server_ids: - if server.os_id not in wizard.command_id.os_ids: - warning_list.append( - _( - "OS %(os)s used by the server '%(srv)s' is not present" - " in the command's OS compatibility list", - os=server.os_id.name, - srv=server.name, - ) - ) - wizard.os_compatibility_warning = ( - "\n".join(warning_list) if warning_list else False - ) - - @api.depends("server_ids") - def _compute_have_access_to_server(self): - """ - Compute have_access_to_server field - """ - for record in self: - if not record.server_ids: - record.have_access_to_server = False - continue - record.have_access_to_server = all( - server._have_access_to_server("write") for server in record.server_ids - ) - - @api.depends( - "custom_variable_value_ids.value_char", - "custom_variable_value_ids.required", - ) - def _compute_has_missing_required_values(self): - """ - Mark the wizard when at least one *required* variable - has an empty value **and** build a human-readable message. - """ - for wiz in self: - missing = wiz.custom_variable_value_ids.filtered( - lambda var_line: var_line.required and not var_line.value_char - ) - wiz.has_missing_required_values = bool(missing) - wiz.missing_required_variables_message = ( - _( - "Please provide values for the following " - "configuration variables: %(vars)s", - vars=", ".join(missing.mapped("variable_id.name")), - ) - if missing - else False - ) - - @api.onchange("action", "applicability") - def _onchange_action(self): - """ - Reset command after change action - """ - self.command_id = False - - @api.onchange("command_variable_ids", "server_ids") - def _onchange_command_variable_ids(self): - """ - Reset custom variable values after change code - """ - # Remove existing custom variable values - self.custom_variable_value_ids = False - - if ( - not self.command_variable_ids - or not self.server_ids - or len(self.server_ids) > 1 - ): - return - - # Add new custom variable values - # Render values for the first server only. - server_id = self.server_ids - - # Get variable list - variables = self.get_variables() - - # Get variable values - variable_values = server_id.get_variable_values(variables.get(str(self.id)))[ - server_id.id - ] - - # Filter variables current user has access to - command_variables = self.command_variable_ids.search( - [("id", "in", self.command_variable_ids.ids)] - ) - - self.custom_variable_value_ids = [ - ( - 0, - 0, - { - "variable_id": variable.id, - "value_char": variable_values.get(variable.reference), - "option_id": variable.option_ids.filtered( - lambda o, v=variable: o.value_char - == variable_values.get(v.reference) - ).id - if variable.variable_type == "o" - else None, - "variable_value_id": server_id.variable_value_ids.filtered( - lambda v, var=variable: v.variable_id == var - )[:1].id, - }, - ) - for variable in command_variables - ] - - def action_run_command(self): - """ - Return wizard action to select command and execute it - """ - context = self.env.context.copy() - context.update( - { - "default_server_ids": self.server_ids.ids, - } - ) - return { - "type": "ir.actions.act_window", - "name": _("Run Command"), - "res_model": "cx.tower.command.run.wizard", - "view_mode": "form", - "target": "new", - "context": context, - } - - def run_command_on_server(self): - """Run command on selected servers""" - # Check if all required values are set - if self.has_missing_required_values: - raise ValidationError(self.missing_required_variables_message) - # Check if command is selected - if not self.command_id: - raise ValidationError(_("Please select a command to execute")) - # Generate custom label. Will be used later to locate the command log - log_label = generate_random_id(4) - path_value = ( - self.env.user.has_group("cetmix_tower_server.group_manager") and self.path - ) - # Add custom values for log - kwargs = { - "log": {"label": log_label}, - "variable_values": { - value.variable_id.reference: value.value_char - for value in self.custom_variable_value_ids - }, - } - for server in self.server_ids: - server.run_command( - self.command_id, - sudo=self.use_sudo, - path=path_value, - **kwargs, - ) - return { - "type": "ir.actions.act_window", - "name": _("Command Log"), - "res_model": "cx.tower.command.log", - "view_mode": "tree,form", - "target": "current", - "context": {"search_default_label": log_label}, - } - - def run_command_in_wizard(self): - """ - Runs a given code as is in wizard - """ - # Check if multiple servers are selected - if len(self.server_ids) > 1: - raise ValidationError( - _("You cannot run custom code on multiple servers at once.") - ) - - # Raise access error if non manager is trying to call this method - if not self.env.user.has_group( - "cetmix_tower_server.group_manager" - ) and not self.env.user.has_group("cetmix_tower_server.group_root"): - raise AccessError(_("You are not allowed to execute commands in wizard")) - - self.ensure_one() - - if not self.command_id.allow_parallel_run: - running_count = ( - self.env["cx.tower.command.log"] - .sudo() - .search_count( - [ - ("server_id", "in", self.server_ids.ids), - ("command_id", "=", self.command_id.id), - ("is_running", "=", True), - ] - ) - ) - # Create log record and continue to the next one - # if the same command is currently running on the same server - # Log result - if running_count > 0: - raise ValidationError( - _("Another instance of the command is already running") - ) - - if not self.rendered_code: - raise ValidationError(_("You cannot execute an empty command")) - - # check that we can execute the command for selected servers - command_servers = self.command_id.server_ids - if command_servers and not all( - [server in command_servers for server in self.server_ids] - ): - raise ValidationError(_("Some servers don't support this command")) - - result = "" - - # Set the "no_split_for_sudo" property - if self.command_id and self.command_id.no_split_for_sudo: - no_split_for_sudo = True - else: - no_split_for_sudo = False - - for server in self.server_ids: - server_name = server.name - # Prepare key renderer values - key_vals = { - "server_id": server.id, - "partner_id": server.partner_id.id if server.partner_id else None, - } - - kwargs = { - "key": key_vals, - "no_split_for_sudo": no_split_for_sudo, - } - - if self.action == "python_code": - command_result = server._run_python_code( - code=self.rendered_code, **kwargs - ) - else: - command_result = server._run_command_using_ssh( - server._get_ssh_client(raise_on_error=True), - self.rendered_code, - self.path or None, - sudo=self.use_sudo and server.use_sudo, - **kwargs, - ) - command_error = command_result["error"] - command_response = command_result["response"] - if command_error: - result = f"{result}\n[{server_name}]: ERROR: {command_error}" - if command_response: - result = f"{result}\n[{server_name}]: {command_response}" - if not result.endswith("\n"): - result = f"{result}\n" - - if result: - self.result = html_converter.convert(result) - return { - "type": "ir.actions.act_window", - "name": _("Run Result"), - "res_model": "cx.tower.command.run.wizard", - "res_id": self.id, # pylint: disable=no-member - "view_mode": "form", - "target": "new", - } - - def _is_privileged_user(self): - """Return True if current user is in Manager or Root group.""" - return self.env.user.has_group( - "cetmix_tower_server.group_manager" - ) or self.env.user.has_group("cetmix_tower_server.group_root") - - -class CxTowerCommandRunWizardVariableValue(models.TransientModel): - """ - Custom variable values for command run wizard - """ - - _inherit = "cx.tower.custom.variable.value.mixin" - _name = "cx.tower.command.run.wizard.variable.value" - _description = "Custom variable values for command run wizard" - - variable_id = fields.Many2one( - readonly=True, - ) - wizard_id = fields.Many2one( - "cx.tower.command.run.wizard", - string="Wizard", - ) diff --git a/addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard_view.xml deleted file mode 100644 index 7926542..0000000 --- a/addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard_view.xml +++ /dev/null @@ -1,237 +0,0 @@ - - - - - cx.tower.command.run.wizard.view.form - cx.tower.command.run.wizard - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    - - - Cetmix Tower Run Command - cx.tower.command.run.wizard - ir.actions.act_window - form - - {'default_server_ids': [active_id]} - new - - -
    diff --git a/addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard.py deleted file mode 100644 index 94dc8c8..0000000 --- a/addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard.py +++ /dev/null @@ -1,149 +0,0 @@ -# Copyright (C) 2022 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, api, fields, models - -from ..models.tools import generate_random_id - - -class CxTowerPlanRunWizard(models.TransientModel): - """ - Wizard to run a flight plan on selected servers. - """ - - _name = "cx.tower.plan.run.wizard" - _description = "Run Flight Plan in Wizard" - - server_ids = fields.Many2many( - "cx.tower.server", - string="Servers", - ) - plan_id = fields.Many2one( - string="Flight Plan", - comodel_name="cx.tower.plan", - required=True, - ) - note = fields.Text(related="plan_id.note", readonly=True) - plan_domain = fields.Binary( - compute="_compute_plan_domain", - ) - tag_ids = fields.Many2many( - comodel_name="cx.tower.tag", - string="Tags", - ) - applicability = fields.Selection( - selection=[ - ("this", "For selected server(s)"), - ("shared", "Non server restricted"), - ], - default="shared", - required=True, - compute="_compute_show_servers", - readonly=False, - store=True, - help="Selected server(s): only Flight Plans that are specific" - " to the selected server(s)\n" - "Non server restricted: all Flight Plans that are " - "not specific to any server", - ) - # Lines - plan_line_ids = fields.One2many( - string="Commands", - comodel_name="cx.tower.plan.line", - compute="_compute_plan_line_ids", - compute_sudo=True, - groups="cetmix_tower_server.group_manager", - ) - show_servers = fields.Boolean( - compute="_compute_show_servers", - compute_sudo=True, - ) - custom_variable_value_ids = fields.One2many( - "cx.tower.plan.run.wizard.variable.value", - "wizard_id", - ) - - @api.model - def default_get(self, fields_list): - res = super().default_get(fields_list) - if not self._is_privileged_user(): - res["applicability"] = "this" - return res - - @api.depends("server_ids") - def _compute_show_servers(self): - for rec in self: - rec.show_servers = bool(rec.server_ids and len(rec.server_ids) > 1) - - @api.depends("plan_id") - def _compute_plan_line_ids(self): - """Sel lines in wizard based on selected plan""" - for rec in self: - if rec.plan_id and rec.plan_id.line_ids: - rec.plan_line_ids = rec.plan_id.line_ids - else: - rec.plan_line_ids = None - - @api.depends("applicability", "server_ids", "tag_ids") - def _compute_plan_domain(self): - """Compose domain based on condition""" - for record in self: - domain = [] - if record.applicability == "shared": - domain = [("server_ids", "=", False)] - elif record.applicability == "this": - domain.append(("server_ids", "in", record.server_ids.ids)) - if record.tag_ids: - domain.append(("tag_ids", "in", record.tag_ids.ids)) - record.plan_domain = domain - - @api.onchange("applicability") - def _onchange_applicability(self): - """Reset plan after change record type""" - self.plan_id = False - - def run_flight_plan(self): - """Run flight plan for selected servers""" - - if self.plan_id and self.server_ids: - # Generate custom label. Will be used later to locate the command log - plan_label = generate_random_id(4) - # Add custom values for log - variable_values = { - value.variable_id.reference: value.value_char - for value in self.custom_variable_value_ids - } - custom_values = { - "plan_log": {"label": plan_label}, - "variable_values": variable_values, - } - for server in self.server_ids: - server.run_flight_plan(self.plan_id, **custom_values) - return { - "type": "ir.actions.act_window", - "name": _("Plan Log"), - "res_model": "cx.tower.plan.log", - "view_mode": "tree,form", - "target": "current", - "context": {"search_default_label": plan_label}, - } - - def _is_privileged_user(self): - """Return True if current user is in Manager or Root group.""" - return self.env.user.has_group( - "cetmix_tower_server.group_manager" - ) or self.env.user.has_group("cetmix_tower_server.group_root") - - -class CxTowerPlanRunWizardVariableValue(models.TransientModel): - """ - Custom variable values for flight plan run wizard - """ - - _inherit = "cx.tower.custom.variable.value.mixin" - _name = "cx.tower.plan.run.wizard.variable.value" - _description = "Custom variable values for plan run wizard" - - wizard_id = fields.Many2one( - "cx.tower.plan.run.wizard", - string="Wizard", - ) diff --git a/addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard_view.xml deleted file mode 100644 index 8ae9f4c..0000000 --- a/addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard_view.xml +++ /dev/null @@ -1,115 +0,0 @@ - - - - cx.tower.plan.run.wizard.view.form - cx.tower.plan.run.wizard - -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    - - Cetmix Tower Run Flight Plan - cx.tower.plan.run.wizard - ir.actions.act_window - form - - {'default_server_ids': [active_id]} - new - -
    diff --git a/addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard.py deleted file mode 100644 index 043f94c..0000000 --- a/addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2025 Cetmix Oy -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0). - -from odoo import _, fields, models - - -class CxTowerServerHostKeyWizard(models.TransientModel): - """Wizard to show host key""" - - _name = "cx.tower.server.host.key.wizard" - _description = "Show Host Key" - - is_error = fields.Boolean() - host_key = fields.Char() - server_id = fields.Many2one("cx.tower.server") - - def action_insert_host_key(self): - """Show the host key""" - self.ensure_one() - self.server_id.write({"host_key": self.host_key, "skip_host_key": False}) - return { - "type": "ir.actions.client", - "tag": "display_notification", - "params": { - "type": "success", - "title": _("Host Key"), - "message": _("Key inserted successfully!"), - "next": {"type": "ir.actions.act_window_close"}, - }, - } diff --git a/addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard_view.xml deleted file mode 100644 index e3d44b6..0000000 --- a/addons/cetmix_tower_server/wizards/cx_tower_server_host_key_wizard_view.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - cx.tower.server.host.key.wizard.form - cx.tower.server.host.key.wizard - -
    - -
    - Check the key before inserting in the server settings. Do not insert the key if you have any doubts! -
    - - -
    -
    - -
    -
    -
    diff --git a/addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py deleted file mode 100644 index 5693a75..0000000 --- a/addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard.py +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright (C) 2024 Cetmix OÜ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, fields, models - - -class CxTowerServerTemplateCreateWizard(models.TransientModel): - """Create new server from template""" - - _name = "cx.tower.server.template.create.wizard" - _description = "Create new server from template" - - server_template_id = fields.Many2one( - "cx.tower.server.template", - string="Server Template", - readonly=True, - ) - name = fields.Char( - string="Server Name", - required=True, - ) - partner_id = fields.Many2one( - "res.partner", - ) - color = fields.Integer(help="For better visualization in views") - os_id = fields.Many2one( - string="Operating System", - comodel_name="cx.tower.os", - ) - tag_ids = fields.Many2many( - comodel_name="cx.tower.tag", - string="Tags", - ) - ip_v4_address = fields.Char(string="IPv4 Address") - ip_v6_address = fields.Char(string="IPv6 Address") - ssh_port = fields.Integer(string="SSH port", default=22) - ssh_username = fields.Char( - string="SSH Username", - required=True, - help="This is required, however you can change this later " - "in the server settings", - ) - ssh_password = fields.Char(string="SSH Password") - ssh_key_id = fields.Many2one( - comodel_name="cx.tower.key", - string="SSH Private Key", - domain=[("key_type", "=", "k")], - ) - ssh_auth_mode = fields.Selection( - string="SSH Auth Mode", - selection=[ - ("p", "Password"), - ("k", "Key"), - ], - default="p", - required=True, - ) - use_sudo = fields.Selection( - string="Use sudo", - selection=[("n", "Without password"), ("p", "With password")], - help="Run commands using 'sudo'", - ) - host_key = fields.Char( - help="Host key to verify the server", - ) - skip_host_key = fields.Boolean( - string="Don't Check Key", - help="Enable to skip host key verification", - ) - line_ids = fields.One2many( - comodel_name="cx.tower.server.template.create.wizard.line", - inverse_name="wizard_id", - string="Configuration Variables", - ) - has_missing_required_values = fields.Boolean( - compute="_compute_has_missing_required_values", - ) - missing_required_variables = fields.Text( - compute="_compute_missing_required_variables_message", - ) - missing_required_variables_message = fields.Text( - compute="_compute_missing_required_variables_message", - ) - - @api.depends("line_ids.value_char", "line_ids.required") - def _compute_has_missing_required_values(self): - """ - Compute whether there are required variables with missing values. - """ - for wizard in self: - missing_vars = wizard.line_ids.filtered( - lambda line: line.required and not line.value_char - ) - wizard.has_missing_required_values = bool(missing_vars) - wizard.missing_required_variables = ", ".join( - missing_vars.mapped("variable_id.name") - ) - - @api.depends("has_missing_required_values") - def _compute_missing_required_variables_message(self): - """ - Computes the user-friendly message for missing required variables. - """ - for wizard in self: - if wizard.has_missing_required_values and wizard.missing_required_variables: - wizard.missing_required_variables_message = _( - "Please provide values for the following " - "configuration variables: %(variables)s", - variables=wizard.missing_required_variables, - ) - else: - wizard.missing_required_variables_message = False - - def action_confirm(self): - """ - Create and open new created server from template - """ - self.ensure_one() - - kwargs = self._prepare_server_parameters() - server = self.server_template_id._create_new_server( - self.name, pick_all_template_variables=False, **kwargs - ) - action = self.env["ir.actions.actions"]._for_xml_id( - "cetmix_tower_server.action_cx_tower_server" - ) - action.update( - {"view_mode": "form", "res_id": server.id, "views": [(False, "form")]} - ) - return action - - def _prepare_server_parameters(self): - """Prepare new server parameters - - Returns: - dict(): New server parameters - """ - res = { - "ip_v4_address": self.ip_v4_address, - "ip_v6_address": self.ip_v6_address, - "ssh_port": self.ssh_port, - "ssh_username": self.ssh_username, - "ssh_password": self.ssh_password, - "ssh_key_id": self.ssh_key_id.id, - "ssh_auth_mode": self.ssh_auth_mode, - "use_sudo": self.use_sudo, - "partner_id": self.partner_id.id, - "os_id": self.os_id.id, - "tag_ids": [(4, tag_id) for tag_id in self.tag_ids.ids], - "skip_host_key": self.skip_host_key, - "host_key": self.host_key if not self.skip_host_key else None, - } - if self.line_ids: - res.update( - { - "configuration_variables": { - line.variable_reference: line.value_char - for line in self.line_ids - }, - "configuration_variable_options": { - line.variable_reference: line.option_id.reference - for line in self.line_ids - if line.option_id - }, - } - ) - return res - - -class CxTowerServerTemplateCreateWizardVariableLine(models.TransientModel): - """Configuration variables""" - - _name = "cx.tower.server.template.create.wizard.line" - _description = "Create new server from template variables" - - wizard_id = fields.Many2one("cx.tower.server.template.create.wizard") - variable_value_id = fields.Many2one( - comodel_name="cx.tower.variable.value", - ) - variable_id = fields.Many2one( - comodel_name="cx.tower.variable", - compute="_compute_variable_id", - readonly=False, - store=True, - ) - variable_reference = fields.Char(related="variable_id.reference", readonly=True) - value_char = fields.Char( - string="Value", - compute="_compute_value_char", - readonly=False, - store=True, - ) - required = fields.Boolean( - related="variable_value_id.required", - help="Indicates if this variable is mandatory for server creation", - readonly=True, - store=True, - ) - variable_type = fields.Selection( - related="variable_id.variable_type", - readonly=True, - ) - option_id = fields.Many2one( - comodel_name="cx.tower.variable.option", - domain="[('variable_id', '=', variable_id)]", - readonly=False, - compute="_compute_variable_id", - store=True, - ) - - @api.depends("variable_value_id") - def _compute_variable_id(self): - for rec in self: - variable_value = rec.variable_value_id - if variable_value: - rec.update( - { - "variable_id": variable_value.variable_id.id, - "option_id": variable_value.option_id.id, - "value_char": variable_value.value_char, - } - ) - - @api.depends("option_id", "variable_id", "variable_type") - def _compute_value_char(self): - for rec in self: - if rec.variable_id and rec.variable_type == "o" and rec.option_id: - rec.value_char = rec.option_id.value_char - else: - rec.value_char = "" - - @api.onchange("variable_id") - def _onchange_variable_id(self): - """ - Reset option_id when variable changes. - """ - self.update({"option_id": None}) - - @api.onchange("value_char") - def _onchange_value_char(self): - """ - Check value before saving - """ - if self.variable_id: - valid, message = self.variable_id._validate_value(self.value_char) - if not valid: - return {"warning": {"title": _("Value is invalid"), "message": message}} diff --git a/addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard_view.xml deleted file mode 100644 index c26839f..0000000 --- a/addons/cetmix_tower_server/wizards/cx_tower_server_template_create_wizard_view.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - cx.tower.server.template.create.wizard.view.form - cx.tower.server.template.create.wizard - -
    -
    -

    - -

    -
    - - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -