\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
+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
+msgid " - Missing variables: %(variables)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "%(jet)s: action failed (status %(status)s)"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_reference_mixin.py:0
+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
+msgid "%(name)s (copy)"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_install.py:0
+msgid "%(timestamp)s %(action)s completed on server '%(server_name)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_install.py:0
+msgid "%(timestamp)s %(action)s failed on server '%(server_name)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "%(timestamp)s Available in the '%(name)s' state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command_log.py:0
+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
+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
+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
+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_jet_template_install.py:0
+msgid "%(timestamp)s Installing template on server '%(server_name)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+msgid ""
+"%(timestamp)s Template is already installed or being installed on the "
+"server '%(server_name)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_install.py:0
+msgid "%(timestamp)s Uninstalling template on server '%(server_name)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "...no jet is assigned yet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0
+msgid "...save record to see the final expression or click the line to edit"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "..no jet is assigned yet"
+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_jet_template_view_form
+msgid "__original_jet__ The reference of the original jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "__original_server__ The reference of the original server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid ""
+"__requested_jet_state__ The reference of the requested state of"
+" the new jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_kanban
+msgid ""
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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"
+" "
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_kanban
+msgid ""
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_kanban
+msgid ""
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_kanban
+msgid ""
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command_log.py:0
+msgid ""
+"
Error converting command response to HTML: %(error)s
"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid "Fr"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid "Mo"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid "Sa"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid "Su"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid "Th"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid "Tu"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid "We"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "for"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_form
+msgid ""
+"Flight Plan\n"
+" Logs"
+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.res_config_settings_view_form
+msgid ""
+"Files will be pulled from server to Tower automatically using cron "
+"job."
+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.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.res_config_settings_view_form
+msgid "Run scheduled tasks"
+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."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban
+msgid "IPv4:"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban
+msgid "IPv6:"
+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
+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
+msgid "A helper shortcut to env['cx.tower.jet']"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "A helper shortcut to env['cx.tower.jet.waypoint']"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+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
+msgid "A helper shortcut to env['cx.tower.server']"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_dependency.py:0
+msgid "A jet cannot depend on a jet with a different template!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_dependency.py:0
+msgid "A jet cannot depend on itself!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_jet_template_dependency_unique_template_dependency
+msgid "A template can only depend on another template once!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_dependency.py:0
+msgid "A template cannot depend on itself!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_jet_template
+msgid ""
+"A variable value cannot be assigned multiple times to the same jet template!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_variable_value_unique_variable_value_jet
+msgid "A variable value cannot be assigned multiple times to the same jet!"
+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_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_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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_jet_action__access_level
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__access_level
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__access_level
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__access_level
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_template__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_jet_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_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_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
+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
+msgid "Access level is not defined for '%(variable)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-javascript
+#: code:addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.esm.js:0
+msgid "Ace Tower Editor"
+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_jet_action_wizard__action_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__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_jet_template_install_view_search
+#: 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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid ""
+"Action '%(action)s' is not available for jet '%(jet)s' in state '%(state)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action_wizard__action_available_ids
+msgid "Action Available"
+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_jet__message_needaction
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_action.py:0
+msgid "Action can be triggered only for a single jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Action failed for jet %(jet)s."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__jet_action_id
+msgid "Action to trigger"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Action triggered for %(jet_references)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__jet_template_id
+msgid "Action will be triggered for all dependent jets of this template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Action with an initial state can be triggered only from that state."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid ""
+"Action without a final state do not change the state. Such actions can be "
+"used to destroy a Jet."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid ""
+"Action without an initial state can be triggered from any state. Such "
+"actions can be used to create a new Jet."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_line__action_ids
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Actions"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_action_wizard__action_available_ids
+msgid "Actions that are available for all selected jets"
+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_jet__active
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__active
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__active
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_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_jet__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_jet__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_jet__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_jet__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_terms:ir.actions.act_window,help:cetmix_tower_server.action_cx_cetmix_tower_partner
+msgid "Add a new partner"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.action_cx_tower_server
+msgid "Add a new server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.action_cx_tower_server_template
+msgid "Add a new server template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Add notes here..."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__metadata
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__metadata_text
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__metadata
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__metadata_text
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_metadata_mixin__metadata
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_metadata_mixin__metadata_text
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__metadata
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server__metadata_text
+msgid "Additional metadata for this record"
+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
+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/wizards/cx_tower_command_run_wizard.py:0
+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_jet_state_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_kanban
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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_jet_template_install_wiz_view_form
+msgid "Are you sure you want to install the template on the selected servers?"
+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_jet_waypoint_template__plan_arrive_id
+msgid "Arrive Flight Plan"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__arriving
+msgid "Arriving"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_scheduled_task.py:0
+msgid "At least one day of week must be selected for the task '%s'."
+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_jet__message_attachment_count
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Auto-generated waypoint"
+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_jet_clone_wizard_variable_line__value_char
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__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_jet__action_available_ids
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Available Actions"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__available_state_ids
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Available States"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__action_available_ids
+msgid ""
+"Available actions for the jet. Click on the button to trigger the action."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__state_available_ids
+msgid ""
+"Available states for the jet. Click on the button to transition to the "
+"state."
+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.actions.act_window,name:cetmix_tower_server.action_cx_tower_jet_bring_to_state
+msgid "Bring to State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__can_fly_to
+msgid "Can Fly To"
+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_jet__reference
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_action__reference
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_state__reference
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__reference
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template_dependency__reference
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__reference
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint_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_jet_action_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_wiz_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_file.py:0
+msgid "Cannot %(action)s %(f)s to/from server: %(err)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0
+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
+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_jet_waypoint.py:0
+msgid ""
+"Cannot change waypoint type for %(waypoint)s because it is not in the draft "
+"state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Cannot create waypoint for jet %s because it is busy"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_tag.py:0
+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_jet_waypoint.py:0
+msgid ""
+"Cannot delete the waypoint %(waypoint)s because it is in the %(state)s state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid ""
+"Cannot delete the waypoint %(waypoint)s because it is the current waypoint "
+"of the jet %(jet)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid ""
+"Cannot delete waypoint %(waypoint)s because it is currently designated as "
+"the destination for jet %(jet)s."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0
+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_jet_waypoint.py:0
+msgid ""
+"Cannot fly to waypoint %(waypoint)s on jet %(jet)s because it is not in the "
+"'ready' state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid ""
+"Cannot fly to waypoint %(waypoint)s on jet %(jet)s because the previous "
+"waypoint %(previous_waypoint)s is not in the 'ready' or 'current' state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid ""
+"Cannot fly to waypoint %(waypoint)s on jet %(jet)s because there is another "
+"waypoint %(other_waypoint)s in the 'arriving' or 'leaving' state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid ""
+"Cannot prepare waypoint %(waypoint)s on jet %(jet)s because it is not in the"
+" 'draft' state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+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
+msgid ""
+"Cannot run command\n"
+". CODE: %(status)s. RESULT: %(res)s. ERROR: %(err)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid ""
+"Cannot set is_destination to True for waypoint %(waypoint)s because it is in"
+" the %(state)s state"
+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
+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_jet
+msgid "Cetmix Tower Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_action
+msgid "Cetmix Tower Jet Action"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_dependency
+msgid "Cetmix Tower Jet Dependency"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_request
+msgid "Cetmix Tower Jet Request"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_state
+msgid "Cetmix Tower Jet State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_template
+msgid "Cetmix Tower Jet Template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_template_dependency
+msgid "Cetmix Tower Jet Template Dependency"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_waypoint
+msgid "Cetmix Tower Jet Waypoint"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_waypoint_template
+msgid "Cetmix Tower Jet Waypoint Template"
+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.fields,field_description:cetmix_tower_server.field_res_users__cetmix_tower_show_jet_available_states
+msgid "Cetmix Tower Show Jet Available States"
+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_metadata_mixin
+msgid "Cetmix Tower metadata 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
+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
+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
+msgid "Cetmix Tower: Run scheduled tasks"
+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
+#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_jet_clone
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+msgid "Clone"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+msgid "Clone Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_clone_wizard
+msgid "Clone jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__jet_cloned_from_id
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+msgid "Cloned from"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Cloning"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Cloning on the same server is not allowed for template '%(template)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid ""
+"Cloning to a different server is not allowed for template '%(template)s'"
+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
+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_jet__color
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__color
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__color
+#: 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_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_jet_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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_tree
+msgid "Command"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0
+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_jet__command_log_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__command_log_ids
+#: 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
+msgid "Command Log"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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 "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
+msgid "Command is not compatible with the server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Command log is required for 'Create a Waypoint' commands!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Command log is required for 'Jet Action' commands!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__created_from_command_log_id
+msgid ""
+"Command log that created this waypoint; the waypoint callback finishes it "
+"when the waypoint reaches ready/current or error. Kept for debugging/audit."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0
+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
+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."
+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,help:cetmix_tower_server.field_cx_tower_jet_template_install__line_ids
+msgid "Complete list of templates to install/uninstall including dependencies"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__date_done
+msgid "Completed on"
+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}} == '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_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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 "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_jet_action_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_wizard_view_form
+#: 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
+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
+msgid "Connection successful."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid ""
+"Connection test passed! \n"
+"%(res)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Connection test passed."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_res_partner
+msgid "Contact"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_template__plan_create_id
+msgid "Create Flight Plan"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__action_create_id
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "Create Jet"
+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
+msgid "Create Server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_search
+msgid "Create from Wizard"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_create_wizard
+msgid "Create new jet"
+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_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_state_action
+msgid "Create your first Jet State!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_template_action
+msgid "Create your first Jet Template!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_action
+msgid "Create your first Jet!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_waypoint_template_action
+msgid "Create your first Waypoint Template!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_waypoint_action
+msgid "Create your first Waypoint!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__created_from_command_log_id
+msgid "Created From"
+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_jet__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action_wizard__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard_variable_line__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__create_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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_jet__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action_wizard__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard_variable_line__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__create_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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
+msgid "Cron Job"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/res_config_settings.py:0
+msgid "Cron job not found"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__current
+msgid "Current"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "Current Cetmix Tower Jet waypoint this command is running on"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "Current Cetmix Tower jet template this command is running on"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "Current Cetmix Tower jet this command is running on"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+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
+msgid "Current Odoo user"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "Current Odoo user ID"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__state_id
+msgid "Current State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__state
+msgid ""
+"Current state of the jet. NB: this is the reference of the state, not the "
+"name."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__waypoint_id
+msgid "Current waypoint of the jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__current_line_id
+msgid "Currently Installing"
+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
+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_jet_waypoint__variable_values
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__variable_values_text
+msgid "Custom variable values for this waypoint"
+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.selection,name:cetmix_tower_server.selection__cx_tower_scheduled_task__interval_type__dow
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid "Days of Week"
+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.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__template_requires_ids
+msgid ""
+"Define other templates that must be in specific states for this template to "
+"function"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__template_required_by_ids
+msgid ""
+"Define other templates that require this template to be in a specific state "
+"to function"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__deletable
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+msgid "Deletable"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Delete"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_template__plan_delete_id
+msgid "Delete Flight Plan"
+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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Delete the Waypoint"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__deleted
+msgid "Deleted"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__deleting
+msgid "Deleting"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Dependencies"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__dependency_graph_image
+msgid "Dependency Graph"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__dependency_graph_svg
+msgid "Dependency Graph Svg"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_request__for_dependency_id
+msgid "Dependency for which request is created"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_dependency__jet_depends_on_id
+msgid "Depends On"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Depends on"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_action__state_to_id
+msgid "Destination state for this transition. Leave blank for a final state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__target_state_id
+msgid "Destination state to which the jet is currently transitioning"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__action_destroy_id
+msgid "Destroy Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Different 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_jet__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action_wizard__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard_variable_line__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_dependency__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_dependency__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__display_name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Do you want to delete this waypoint? This action cannot be undone."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Do you want to fly to this waypoint?"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Do you want to prepare this waypoint?"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Do you want to set this state?"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Do you want to trigger this action?"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_create_wizard__jet_template_domain
+msgid "Domain for jet template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_create_wizard__server_domain
+msgid "Domain for server"
+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.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install__state__done
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install_line__state__done
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_search
+msgid "Done"
+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
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__draft
+msgid "Draft"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0
+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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_request.py:0
+msgid "Either a jet or a jet template must be provided to create a request"
+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:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__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
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__state_error_id
+msgid "Error State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+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_jet__current_action_id
+msgid "Executing Action"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__current_command_log_id
+msgid "Executing Command Log"
+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:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_request__state__failed
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install__state__failed
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install_line__state__failed
+#: 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_jet_template_install_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_search
+#: 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.view_cx_tower_jet_request_search
+msgid "Failed"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0
+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
+msgid "Failed to connect. Error: %(err)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard.py:0
+msgid "Failed to create jet. Please check the server and template settings."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+msgid "Failed to generate unique jet name after %(attempts)d attempts"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Failed to leave current waypoint."
+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_scheduled_task.py:0
+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
+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
+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
+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
+msgid "File already exists"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_file_template.py:0
+msgid "File already exists on server."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+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
+msgid "File created and uploaded successfully"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0
+msgid "File deleted!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0
+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
+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
+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_jet__file_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__file_ids
+#: 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_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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
+msgid "Files deleted!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0
+msgid "Files downloaded!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__file_ids
+msgid "Files of this jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__file_ids
+msgid "Files of this jet template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_file.py:0
+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 automatically using cron job."
+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_jet_action__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_jet_view_form
+#: 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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_tree
+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_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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 "Flight Plan Logs"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_install.py:0
+msgid "Flight Plan Logs - %(install_name)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_plan_log.py:0
+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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_plan_log_view_tree
+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
+msgid "Flight Plans Stopped"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0
+msgid "Flight plan is not compatible with the server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid ""
+"Flight plan is run on the cloned jet, not on the original one. Following "
+"variables are available in the flight plan:"
+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
+msgid "Flight plan running error"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+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_jet_action__plan_id
+msgid "Flight plan to execute when this action is triggered"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint_template__plan_create_id
+msgid "Flight plan to run after waypoint is created"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint_template__plan_arrive_id
+msgid "Flight plan to run after waypoint is reached"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint_template__plan_delete_id
+msgid "Flight plan to run before deleting the waypoint"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint_template__plan_leave_id
+msgid "Flight plan to run before leaving the waypoint"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__plan_clone_same_server_id
+msgid "Flight plan used to clone the jet on the same server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__plan_clone_different_server_id
+msgid "Flight plan used to clone the jet to a different server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__plan_install_id
+msgid "Flight plan used to install the template from a server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__plan_uninstall_id
+msgid "Flight plan used to uninstall the template from a server"
+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
+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_command__fly_here
+msgid "Fly Here"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Fly here"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Fly to this waypoint"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Flying to waypoint %(waypoint)s on jet %(jet)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_jet__message_follower_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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_jet__message_partner_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Following jets cannot be deleted as they are not deletable: %s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Following keyword is not allowed in Python code: '%(banned_keyword)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+msgid ""
+"Following templates cannot be deleted as they are installed on servers: %s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+msgid "Following templates cannot be deleted as they still have jets: %s"
+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_jet__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,field_description:cetmix_tower_server.field_cx_tower_jet_request__for_dependency_id
+msgid "For Dependency"
+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_scheduled_task__friday
+msgid "Friday"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__state_from_id
+msgid "From State"
+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_jet_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 "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_jet_template_install_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_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
+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_jet__has_message
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_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
+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"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "I want a new"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_clone_wizard__name_type__m
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_clone_wizard__url_type__m
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_create_wizard__name_type__m
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_create_wizard__url_type__m
+msgid "I will put myself"
+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_jet__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action_wizard__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard_variable_line__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_dependency__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_dependency__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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_jet__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,field_description:cetmix_tower_server.field_cx_tower_jet__icon
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__icon
+msgid "Icon image"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__icon
+msgid ""
+"Icon of the related product to make navigation easier. E.g. Docker logo for "
+"the Docker jet template."
+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_jet__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_jet__message_needaction
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__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_jet__message_has_error
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__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
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__show_in_create_wizard
+msgid ""
+"If enabled, the template will be shown in the wizard to create a new jet"
+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
+msgid "If exit code"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/tests/test_plan.py:0
+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:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__is_destination
+msgid "Indicates if this waypoint is the current destination"
+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.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install__action__install
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_wiz_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Install"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_wiz_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form
+msgid "Install Jet Template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_template_install_wiz
+msgid "Install Jet Template on Selected Servers"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_jet_template_install_wiz_action
+msgid "Install on Servers"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_install.py:0
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Installation"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__plan_install_id
+msgid "Installation Flight Plan"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Installation Logs"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_form
+msgid "Installation Steps"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__install_ids
+msgid "Installations"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__install_ids
+msgid "Installations of the template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__jet_template_ids
+msgid "Installed Jet Templates"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__server_ids
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Installed on Servers"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_action__state_transit_id
+msgid "Intermediate state during the transition"
+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_jet_template.py:0
+msgid ""
+"Invalid URL: '%(url)s'. URL must contain a protocol and a proper domain or "
+"IP, eg 'https://my_tower_jet.example.com'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_file_template.py:0
+msgid ""
+"Invalid if_file_exists value: %(if_file_exists)s. Expected one of "
+"%(valid_behaviors)s."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_variable.py:0
+msgid "Invalid value!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__is_destination
+msgid "Is Destination"
+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_jet__message_is_follower
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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_jet__is_waypoints_available
+msgid "Is Waypoints Available"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Is final"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Is initial"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__jet_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__jet_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action_wizard__jet_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__jet_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_dependency__jet_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_dependency__template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__jet_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__jet_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__jet_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__jet_id
+#: 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_jet_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_jet_request_search
+msgid "Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_plan.py:0
+msgid "Jet %(jet)s does not belong to server %(server)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Jet %(jet)s has no dependent jets with template %(template)s."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_request.py:0
+msgid "Jet %(jet)s is not on server %(server)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Jet %(jet)s was moved to the '%(state)s' state."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Jet '%(jet)s' doesn't belong to the server '%(server)s'."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0
+msgid "Jet '%(jet)s' is currently executing an action"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__jet_action_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__jet_action_id
+msgid "Jet Action"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__jet_count
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__jet_count
+msgid "Jet Count"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Jet Logs"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_jet_request
+#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_jet_request
+msgid "Jet Requests"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_view_form
+msgid "Jet State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_jet_state_action
+msgid "Jet States"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_state_action
+msgid ""
+"Jet States represent the different states a jet can be in during its "
+"lifecycle."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_log__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_file__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_template__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server_log__jet_template_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable_value__jet_template_id
+#: 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_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_jet_request_search
+msgid "Jet Template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__jet_template_count
+msgid "Jet Template Count"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_dependency__jet_template_dependency_id
+msgid "Jet Template Dependency"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__jet_template_domain
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__jet_template_domain
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__jet_template_domain
+msgid "Jet Template Domain"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__jet_template_install_id
+msgid "Jet Template Install"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__jet_template_install_id
+msgid "Jet Template Install Job"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_template_install
+msgid "Jet Template Install/Uninstall"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_template_install_line
+msgid "Jet Template Install/Uninstall Line"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__jet_template_install_id
+msgid "Jet Template Install/Uninstall record being run. "
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_form
+msgid "Jet Template Installation"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_jet_template_install
+msgid "Jet Template Installations"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__jet_template_message
+msgid "Jet Template Message"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__jet_template_state_ids
+msgid "Jet Template State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_jet_template_action
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__jet_template_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_cx_tower_scheduled_task_view_form
+msgid "Jet Templates"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_template_action
+msgid "Jet Templates are used to create and manage Jets."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__url
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Jet URL, eg 'https://meme.example.com'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+#: code:addons/cetmix_tower_server/tests/test_server_jet_action_command.py:0
+msgid "Jet action is not found."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Jet dependencies are not satisfied"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Jet for which command is run is not found."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_kanban
+msgid "Jet icon"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__icon
+msgid "Jet icon, computed from the template by default"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Jet limit per server reached for '%(jet)s' on server '%(server)s'!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.action_cx_tower_jet_request
+msgid ""
+"Jet requests will appear here when jets request resources from templates."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Jet template and server cannot be changed once the jet is created!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_kanban
+msgid "Jet template icon"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Jet template is not found."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_action__jet_template_id
+msgid "Jet template that this action belongs to"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__jet_template_id
+msgid "Jet template this file belongs to"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint_template__jet_template_id
+msgid "Jet template this waypoint template belongs to"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form
+msgid "Jet template will be uninstalled from the server. Are you sure?"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_request__jet_id
+msgid "Jet that is requested"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_request__requested_by_jet_id
+msgid "Jet that is requesting the jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_dependency__jet_depends_on_id
+msgid "Jet this Jet depends on."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_dependency__jet_id
+msgid "Jet this dependency belongs to"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_file__jet_id
+msgid "Jet this file belongs to"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__jet_cloned_from_id
+msgid ""
+"Jet this jet was cloned from. This field is set when the jet is cloned from "
+"another jet."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__jet_id
+msgid "Jet this waypoint belongs to"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+msgid "Jet will be cloned. Are you sure?"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_jet_action
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command_run_wizard__jet_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__jet_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__jet_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__jet_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__jet_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__jet_ids
+#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_jet
+#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_jet_root
+#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_jet_settings_root
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_tree
+#: 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 "Jets"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_action
+msgid ""
+"Jets represent application instances that are managed independently\n"
+" from their host servers."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__jet_required_by_ids
+msgid "Jets that depend on this jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_run_wizard__jet_ids
+msgid "Jets to run the command on"
+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
+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_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_jet__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action_wizard__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard_variable_line__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__write_uid
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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_jet__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action_wizard__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard_variable_line__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__write_date
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_jet_create
+#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_create_jet
+msgid "Launch New Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_template__plan_leave_id
+msgid "Leave Flight Plan"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_form
+msgid "Leave blank to autogenerate"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__leaving
+msgid "Leaving"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__action_ids
+msgid "Lifecycle Actions"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__limit_per_server
+msgid "Limit per 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,help:cetmix_tower_server.field_cx_tower_jet_template_install__current_line_id
+msgid "Line that is currently being installed"
+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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "Logger object. Use with caution! Only for debugging purposes."
+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_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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_access_mixin.py:0
+#: model:res.groups,name:cetmix_tower_server.group_manager
+msgid "Manager"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_view_search
+msgid "Manager Access"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/tests/common_jets.py:0
+msgid "Manager must have access to both jets before creating"
+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_jet__manager_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_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_jet__manager_ids
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_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,help:cetmix_tower_server.field_cx_tower_jet_template__limit_per_server
+msgid ""
+"Maximum number of Jets that can be launched on a server. Set to 0 for no "
+"limit."
+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_jet__message_has_error
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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_jet_create_wizard__jet_template_message
+msgid "Message for the user"
+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_jet__message_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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_jet__metadata
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__metadata
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_metadata_mixin__metadata
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__metadata
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form
+msgid "Metadata"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__metadata_text
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__metadata_text
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_metadata_mixin__metadata_text
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__metadata_text
+msgid "Metadata Text"
+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
+#. odoo-javascript
+#: code:addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.esm.js:0
+msgid "Mode"
+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,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__monday
+msgid "Monday"
+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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_variable_value.py:0
+msgid ""
+"Multiple records found with Variable '%(variable_name)s' and Server "
+"'%(server_name)s' with both Jet and Jet Template empty."
+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_jet__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_jet__name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_dependency__name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__name
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__name_type
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__name_type
+msgid "Name Type"
+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_jet_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+msgid "Name/Reference/URL"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_request__state__new
+msgid "New"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form
+msgid "New Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "New Jet will be created. Are you sure?"
+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_jet__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_jet__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_jet__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_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_calendar
+msgid "Next Call"
+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:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_clone_wizard__same_server__n
+#: 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
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.action_cx_tower_jet_request
+msgid "No jet requests found"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard.py:0
+msgid ""
+"No jet templates are currently configured as 'Show in Wizard'. Please check "
+"your jet template settings."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard.py:0
+msgid ""
+"No jet templates configured as 'Show in Wizard' are installed on the "
+"selected server. Please check your jet template settings."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+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_jet.py:0
+msgid "No path found to bring the jet %(jet)s to the state '%(state)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+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
+msgid "No secrets found"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0
+msgid "No server found for the provided reference."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+msgid "No server selected"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_template_install_action
+msgid "No template installations found!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-javascript
+#: code:addons/cetmix_tower_server/static/src/components/ace_variables/autocomplete_popup.xml:0
+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
+msgid "Non-sticky"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+msgid "Not Deletable"
+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_jet__note
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__note
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__note
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__note
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__note
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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.cx_tower_jet_state_view_form
+msgid "Notes"
+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_jet__message_needaction_counter
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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_jet__message_has_error_counter
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__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_jet__message_needaction_counter
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__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_jet__message_has_error_counter
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__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
+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
+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
+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
+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
+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
+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
+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
+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_jet_view_form
+#: 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_jet_view_kanban
+msgid "Open Jet URL"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+#: 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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_kanban
+msgid "Open jets"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Open jets that depend on this jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Open jets that this jet depends on"
+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_jet_clone_wizard_variable_line__option_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__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
+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_jet_template_install_line__order
+msgid "Order"
+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,help:cetmix_tower_server.field_cx_tower_jet__jet_requires_ids
+msgid "Other jets this jet depends on"
+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_jet__partner_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__partner_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__partner_id
+#: 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_jet_clone_wizard__partner_id
+msgid "Partner associated with the cloned jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_create_wizard__partner_id
+msgid "Partner associated with the jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__partner_id
+msgid "Partner associated with this jet"
+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_jet_template__plan_clone_different_server_id
+msgid "Plan Clone Different Server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__plan_clone_same_server_id
+msgid "Plan Clone Same Server"
+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
+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_jet__plan_log_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__plan_log_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_server__plan_log_ids
+msgid "Plan Log"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Plan failed while arriving."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Plan failed."
+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
+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
+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
+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
+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
+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
+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
+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
+msgid "Please select a command to execute"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard.py:0
+msgid "Please select a server to create a jet."
+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_jet_view_form
+msgid "Prepare"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Prepare Waypoint"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__preparing
+msgid "Preparing"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Preparing waypoint %(waypoint)s on jet %(jet)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_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.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__priority
+msgid "Priority"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_request__state__processing
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install__state__processing
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install_line__state__processing
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_search
+msgid "Processing"
+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_jet_action_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_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
+#: 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_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+#: 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
+msgid "Python 'datetime' library"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "Python 'dateutil' library"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+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.
"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+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
+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
+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
+msgid "Python 're' library for regex operations"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+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
+msgid "Python 'time' library"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "Python 'timezone' library"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid ""
+"Python 'tldextract' library. Use tldextract.extract() to parse "
+"domains. Check tldextract for more information."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+msgid "Python 'urllib.parse' library methods."
+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
+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
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_waypoint__state__ready
+msgid "Ready"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_plan_line.py:0
+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_jet__reference
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__reference
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__reference
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__reference
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_dependency__reference
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__reference
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_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_jet_action_reference_unique
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_jet_reference_unique
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_jet_state_reference_unique
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_jet_template_dependency_reference_unique
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_jet_template_reference_unique
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_jet_waypoint_reference_unique
+#: model:ir.model.constraint,message:cetmix_tower_server.constraint_cx_tower_jet_waypoint_template_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_cv_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_jet_state_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_dependency__jet_template_dependency_id
+msgid ""
+"Related jet template dependency. Used to track dependency changes at the "
+"template level."
+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,help:cetmix_tower_server.field_cx_tower_jet__served_jet_request_id
+msgid "Request this jet is currently serving"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_jet_request_search
+msgid "Requested By Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__jet_template_id
+msgid "Requested Template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__requested_by_jet_id
+msgid "Requested by Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_clone_wizard__state_id
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_create_wizard__state_id
+msgid "Requested state of the jet"
+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_jet_clone_wizard_variable_line__required
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__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_jet__jet_required_by_ids
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Required By"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_dependency__template_required_id
+msgid "Required Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_dependency__state_required_id
+msgid "Required State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__template_required_by_ids
+msgid "Required by"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__jet_requires_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__template_requires_ids
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Requires"
+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_jet__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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_access_mixin.py:0
+#: 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_jet_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_view_search
+msgid "Root Access"
+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_jet.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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_run_wizard_view_form
+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_jet.py:0
+#: 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
+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.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
+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
+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
+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
+msgid "SSH run command error %(err)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__dependency_graph_svg
+msgid "SVG image content of the dependency graph of the template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__dependency_graph_image
+msgid "SVG image of the dependency graph of the template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__same_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "Same Server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__saturday
+msgid "Saturday"
+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_jet__scheduled_task_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__scheduled_task_ids
+#: 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_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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 "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
+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."
+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_jet_state_view_search
+msgid "Search Jet States"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_search
+msgid "Search Jet Templates"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+msgid "Search Jets"
+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_jet_template_install_view_search
+msgid "Search Template Installations"
+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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_search
+msgid "Search Waypoint Templates"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_view_search
+msgid "Search Waypoints"
+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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_wiz_view_form
+msgid "Select a jet template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_wizard_view_form
+msgid ""
+"Select a state to set for the selected jets. Only states that appear in the\n"
+" \"State to\" field of jet templates of all selected jets are available."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_wizard_view_form
+msgid "Select a state..."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_action_wizard_view_form
+msgid "Select an action..."
+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_jet__sequence
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state__sequence
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint_template__sequence
+#: 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_jet__served_jet_request_id
+msgid "Served Jet Request"
+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_jet__server_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__server_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__server_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__server_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__server_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__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_jet_template_install_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+#: 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.view_cx_tower_jet_request_search
+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_jet__server_allowed_domain
+msgid "Server Allowed Domain"
+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_jet_create_wizard__server_domain
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_wiz__server_domain
+msgid "Server Domain"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__server_log_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__server_log_ids
+#: 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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_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
+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_jet_template_install__server_id
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template_install_line__server_id
+msgid "Server to install/uninstall the template on"
+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.model.fields,help:cetmix_tower_server.field_cx_tower_jet_request__server_id
+msgid "Server where the jet is requested"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__server_id
+msgid "Server where this jet is running"
+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_jet_template_install_wiz__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_jet_view_form
+msgid "Serves dependencies"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__jet_id
+msgid "Serviced by Jet"
+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_jet_state_wizard_view_form
+msgid "Set Jet State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_state_wizard
+msgid "Set Jet State Wizard"
+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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Set state"
+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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_shortcut.py:0
+msgid "Shortcut '%(shr)s' triggered. Check %(t)s log for result"
+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:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__show_available_states
+msgid "Show Available States"
+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_jets
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_run_wizard__show_jets
+msgid "Show Jets"
+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:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__show_available_states
+#: model:ir.model.fields,help:cetmix_tower_server.field_res_users__cetmix_tower_show_jet_available_states
+msgid "Show available states in the jet view"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__show_in_create_wizard
+msgid "Show in Wizard"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_kanban
+msgid "Show in create wizard"
+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
+msgid "Some servers don't support this command"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_state.py:0
+msgid ""
+"Some states are still used in the following actions: %(actions)s\n"
+"Jet templates: %(templates)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0
+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:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_action__state_from_id
+msgid "Source state for this transition. Leave blank for an initial state"
+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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_tree
+msgid "Started"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__state_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__state_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__state
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_state_wizard__state_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__state
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install_line__state
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__state
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_jet_request_search
+msgid "State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "State '%(state)s' not found for jet '%(jet)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__state_available_ids
+msgid "State Available"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__state_domain
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__state_domain
+msgid "State Domain"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__state
+msgid "State Reference"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_request__state_requested_id
+msgid "State Requested"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_state.py:0
+msgid "State can be set only for a single jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_wizard_view_form
+msgid "State of selected jets will be changed. Are you sure?"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_request__state_requested_id
+msgid "State of the jet that is requested"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_action__state_error_id
+msgid "State to transition to if an error occurs"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_jet_state
+msgid "States"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "States and Actions"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_state_wizard__available_state_ids
+msgid ""
+"States that appear in the 'state_to' field of jet templates of all selected "
+"jets"
+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_jet__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
+msgid "Sticky"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: 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_plan_log_view_tree
+msgid "Stop"
+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
+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_scheduled_task.py:0
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_request__state__success
+#: 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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_jet_request_search
+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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Successfully arrived at waypoint %(waypoint)s on jet %(jet)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Successfully deleted waypoint %(waypoint)s on jet %(jet)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Successfully flew to waypoint %(waypoint)s on jet %(jet)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Successfully prepared waypoint %(waypoint)s on jet %(jet)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__sunday
+msgid "Sunday"
+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_jet__tag_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_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_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_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_jet__target_state_id
+msgid "Target State"
+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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+msgid "Template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+msgid ""
+"Template '%(template_name)s' is not installed on the server "
+"'%(server_name)s'"
+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_jet_template_install_action
+msgid "Template Installations"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_template_install_action
+msgid ""
+"Template installations are created automatically when you install jet "
+"templates on servers."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_request__jet_template_id
+msgid ""
+"Template of the jet that is requested. Used to create a new jet if not "
+"found."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__jet_template_id
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_clone_wizard__jet_template_id
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__jet_template_id
+msgid "Template that this jet is based on"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template_install__jet_template_id
+msgid "Template to install/uninstall"
+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_jet_template
+#: model:ir.ui.menu,name:cetmix_tower_server.menu_cx_tower_server_template
+msgid "Templates"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template_install__line_ids
+msgid "Templates to install"
+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:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template_dependency__template_required_id
+msgid "The Jet template that is required to be in a specific state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template_dependency__template_id
+msgid "The Jet template that requires another template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_clone_wizard__url
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_create_wizard__url
+msgid "The URL of the jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_variable_option.py:0
+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
+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
+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.fields,help:cetmix_tower_server.field_cx_tower_jet_template__action_create_id
+msgid "The action is used to create a new Jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__action_destroy_id
+msgid "The action is used to destroy a Jet"
+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
+msgid "The file %(f_path)s not found."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_create_wizard__name
+msgid "The name of the jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_clone_wizard__name
+msgid "The name of the new jet"
+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
+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:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template_dependency__state_required_id
+msgid "The state of the required Jet"
+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
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+msgid ""
+"There are other templates that depend on template '%(template_name)s' that "
+"are installed on the server '%(server_name)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template.py:0
+msgid ""
+"There are still jets of template '%(template_name)s' on the server "
+"'%(server_name)s'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__server_ids
+msgid "These servers have this jet template installed"
+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.constraint,message:cetmix_tower_server.constraint_cx_tower_jet_dependency_unique_jet_dependency
+msgid "This dependency already exists!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_dependency.py:0
+msgid ""
+"This dependency would create a circular reference chain! Template "
+"'%(template)s' would indirectly depend on itself."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__deletable
+msgid ""
+"This field is set by the jet actions. If enabled, the jet can be deleted"
+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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "This jet can be deleted."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_server_log__jet_template_id
+msgid "This jet template will be used to create log files when jet is created"
+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,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__thursday
+msgid "Thursday"
+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.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install_line__state__to_process
+msgid "To Process"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__state_to_id
+msgid "To State"
+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:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_action__state_transit_id
+msgid "Transit State"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.actions.act_window,name:cetmix_tower_server.action_cx_tower_jet_trigger_action
+msgid "Trigger Action"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_action_wizard_view_form
+msgid "Trigger Jet Action"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_action_wizard
+msgid "Trigger Jet Action Wizard"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Trigger action"
+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_jet_action_wizard_view_form
+msgid "Trigger the action for the selected jets?"
+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_scheduled_task__tuesday
+msgid "Tuesday"
+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_jet_clone_wizard_variable_line__variable_type
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__variable_type
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__waypoint_template_id
+#: 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_jet__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_jet__url
+#: 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
+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
+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
+msgid ""
+"Unable to upload file '%(f)s'.\n"
+"Upload operation is not supported for 'server' type files."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Undefined"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_template_install__action__uninstall
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_form
+msgid "Uninstall"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_install_view_search
+msgid "Uninstall Action"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_install.py:0
+msgid "Uninstallation"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__plan_uninstall_id
+msgid "Uninstallation Flight Plan"
+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_jet_clone_wizard__url
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__url
+msgid "Url"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__url_type
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__url_type
+msgid "Url Type"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__use_custom_variables
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__use_custom_variables
+msgid "Use Custom Variables"
+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:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__sequence
+msgid "Used to sort jets in views"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint_template__sequence
+msgid "Used to sort waypoints in views"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__jet_action_id
+msgid "Used to track flight plans executed by jet actions"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_access_mixin.py:0
+#: model:ir.model,name:cetmix_tower_server.model_res_users
+#: model:res.groups,name:cetmix_tower_server.group_user
+msgid "User"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_search
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_view_search
+msgid "User Access"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_command.py:0
+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_jet__user_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_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_jet__user_ids
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_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_jet_clone_wizard_variable_line__value_char
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__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
+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_jet_clone_wizard_variable_line__variable_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__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
+msgid ""
+"Variable '%(var)s' can only be assigned to one of the models at a time: "
+"Server, Jet, Jet Template, Server Template, or Plan Line Action."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_variable_mixin.py:0
+msgid "Variable '%(variable_reference)s' not found"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_clone_wizard__line_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard__line_ids
+msgid "Variable Lines"
+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_jet_clone_wizard_variable_line__variable_value_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__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
+#: 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_jet__variable_value_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__variable_value_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_view_form
+msgid "Variable Values"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_waypoint__variable_values_text
+msgid "Variable Values Text"
+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,name:cetmix_tower_server.model_cx_tower_jet_clone_wizard_variable_line
+#: model:ir.model,name:cetmix_tower_server.model_cx_tower_jet_create_wizard_variable_line
+msgid "Variable lines"
+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
+msgid "Variable not found"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server_template.py:0
+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
+msgid "Variable syntax error: %s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0
+msgid "Variable value created"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cetmix_tower.py:0
+msgid "Variable value updated"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__variable_value_ids
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__variable_value_ids
+#: 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
+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_jet_template_install.py:0
+msgid "View Installation"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "View Jet"
+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
+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,field_description:cetmix_tower_server.field_cx_tower_command_log__waypoint_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__waypoint_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_plan_log__waypoint_id
+msgid "Waypoint"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid ""
+"Waypoint %(existing)s is already set as the destination for jet %(jet)s. "
+"Only one destination waypoint is allowed per jet."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__waypoint_template_id
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_view_search
+msgid "Waypoint Template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_jet_waypoint_template_action
+msgid "Waypoint Templates"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_waypoint_template_action
+msgid "Waypoint Templates define waypoints for jet templates."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid ""
+"Waypoint creation failed (e.g. waypoint template does not match jet "
+"template)."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py:0
+msgid "Waypoint reached %s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid ""
+"Waypoint template %(waypoint_template)s does not belong to the jet template "
+"%(jet_template)s"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+msgid "Waypoint template %s not found"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_server.py:0
+msgid "Waypoint template is not set."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_waypoint__waypoint_template_id
+msgid "Waypoint template this waypoint is based on"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command__waypoint_template_id
+msgid ""
+"Waypoint template to create the waypoint from. Used when action is Create a "
+"Waypoint."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_command_log__waypoint_id
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__waypoint_id
+msgid "Waypoint this plan log belongs to"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.actions.act_window,name:cetmix_tower_server.cx_tower_jet_waypoint_action
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet__waypoint_ids
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_template__waypoint_template_ids
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "Waypoints"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet__waypoint_ids
+msgid "Waypoints of the jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_jet_template__waypoint_template_ids
+msgid "Waypoints of the template"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.actions.act_window,help:cetmix_tower_server.cx_tower_jet_waypoint_action
+msgid "Waypoints represent waypoints for jets."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_scheduled_task__wednesday
+msgid "Wednesday"
+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__fly_here
+msgid ""
+"When enabled, the created waypoint is set as current (fly to) after "
+"creation."
+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.cx_tower_server_search_view
+msgid "With Jet Templates"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_search_view
+msgid "With Jets"
+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_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_search
+msgid "With URL"
+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_jet_clone_wizard_variable_line__wizard_id
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_jet_create_wizard_variable_line__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
+msgid "Wrong action"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_clone_wizard__same_server__y
+msgid "Yes"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0
+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
+msgid "You are not allowed to modify the server log output."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet.py:0
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_state.py:0
+msgid "You are not allowed to set the '%(state)s' state!"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid ""
+"You can also use jet.jet_cloned_from_id to get the original jet"
+" in Python commands."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0
+msgid "You cannot execute an empty command"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_jet_template_dependency.py:0
+msgid ""
+"You cannot modify an existing template dependency! Please remove it and "
+"create a new one."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0
+msgid "You cannot run custom code on multiple jets at once."
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py:0
+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
+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_jet_template_view_form
+msgid ""
+"You need to save your changes to be able to select newly added actions in "
+"the fields below."
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_action_view_form
+msgid "action name, e.g. 'Start'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_clone_wizard__use_custom_variables__y
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_create_wizard__use_custom_variables__y
+msgid "custom settings"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_clone_wizard__use_custom_variables__n
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_create_wizard__use_custom_variables__n
+msgid "default settings"
+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_jet_template_view_form
+msgid "e.g. Odoo Instance"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "e.g. Production Odoo"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_state_view_form
+msgid "e.g. Running"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "eg 'https://myjet.example.com'"
+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
+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_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "in the state"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_action_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "leave blank to autogenerate"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_view_form
+msgid "managers who can modify this jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "managers who can modify this jet 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 "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_jet_clone_wizard_view_form
+msgid "on the same server"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "on the server"
+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_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "put the name here"
+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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "select a partner if required"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "that will use"
+msgstr ""
+
+#. module: cetmix_tower_server
+#. odoo-python
+#: code:addons/cetmix_tower_server/models/cx_tower_plan_line_action.py:0
+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_jet_clone_wizard_view_form
+msgid "to"
+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_jet_view_form
+msgid "users who can access this jet"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_template_view_form
+msgid "users who can access this jet template"
+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_jet_waypoint_view_form
+msgid "waypoint name, e.g. 'Snapshot before update'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_waypoint_template_view_form
+msgid "waypoint template name, e.g. 'Snapshot'"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_clone_wizard__name_type__a
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_clone_wizard__url_type__a
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_create_wizard__name_type__a
+#: model:ir.model.fields.selection,name:cetmix_tower_server.selection__cx_tower_jet_create_wizard__url_type__a
+msgid "will be auto-generated"
+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 ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "with the URL that"
+msgstr ""
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_clone_wizard_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_jet_create_wizard_view_form
+msgid "with the name that"
+msgstr ""
diff --git a/addons/cetmix_tower_server/i18n/de.po b/addons/cetmix_tower_server/i18n/de.po
new file mode 100644
index 0000000..2585d42
--- /dev/null
+++ b/addons/cetmix_tower_server/i18n/de.po
@@ -0,0 +1,3462 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * cetmix_tower_server
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.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"
+" "
+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}} == '14.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
new file mode 100644
index 0000000..fedcdf2
--- /dev/null
+++ b/addons/cetmix_tower_server/i18n/fi.po
@@ -0,0 +1,3449 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * cetmix_tower_server
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.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"
+" "
+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}} == '14.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
new file mode 100644
index 0000000..ff65c72
--- /dev/null
+++ b/addons/cetmix_tower_server/i18n/hr.po
@@ -0,0 +1,3591 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * cetmix_tower_server
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.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"
+" "
+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}} == '14.0'"
+msgstr ""
+"Uvijeti pod kojima će se ovaj PlanLeta pokrenuti, nrp: {{ odoo_version }} == "
+"'14.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
new file mode 100644
index 0000000..b527687
--- /dev/null
+++ b/addons/cetmix_tower_server/i18n/it.po
@@ -0,0 +1,5305 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * cetmix_tower_server
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2025-11-17 10:49+0100\n"
+"Last-Translator: Stefano Consolaro \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 codice comando Python restituisce il valoreresult che è un dizionario.\n"
+" Ci sono due chiavi nel dizionario:\n"
+"
\n"
+"
exit_code: Intero. Codice uscita del comando. \"0\" significa successo, altri valori indicano un fallimento. Il valore predefinito è \"0\".
\n"
+"
message: Stringa. Messaggio da registrare. Il valore predefinito è \"None\".
\n"
+"
\n"
+"Si può anche accedere al dizionario custom_values che contiene valori personalizzati forniti dal comando o dal piano di volo.\n"
+"I valori predefiniti possono essere modificati, quindi possono essere usati per passare dati tra i 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 salvati nel data base.\n"
+" \n"
+"Qui un esempiuo di un 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 fornito per \" + server_name}\n"
+" custom_values[\"build_name\"] = \"Nuovo nome creato\"\n"
+"\n"
+"\n"
+" \n"
+"Fare riferimento alla documentazione ufficiale per altre informazioni ed esempi.\n"
+"
\n"
+"
Diversi campi possono usare il codice Python o le 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 ""
+"# Fare riferimtno alla sezione 'Aiuto' e alla documentazione per ulteriori informazioni.\n"
+"#\n"
+"# Si può restituire il risultato del comando nella variabile 'result' che è un dizionario:\n"
+"# result = {\"exit_code\": 0, \"message\": \"Un messaggio\"}\n"
+"# default value is {\"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 (copy %(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 "%(shr)s attivato"
+
+#. 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 si è concluso con successo.\n"
+"-100 errore generale,\n"
+"-101 non trovato,\n"
+"-201 altra interfaccia o comando in esecuzione,\n"
+"-202 esecutore per l'azione comando non trovato,\n"
+"-203 esecuzione codice Python fallita\n"
+"-205 controllo condizione riga piano fallito\n"
+"503 se si è verificato un errore di connessione SSH601 se il lavoro in coda è fallito"
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,help:cetmix_tower_server.field_cx_tower_plan_log__plan_status
+#, fuzzy
+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 si è concluso con successo. \n"
+"-301 se un'altra istanza o questo piano di volo è in esecuzione, \n"
+"-302 se il piano è vuoto, \n"
+"-303 se manca il piano di riferimento, \n"
+"-304 se manca il riferimento alla riga del piano, \n"
+"-306 se il piano non è compatibile con il server, \n"
+"-308 se il piano è stato 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_plan_view_form
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.view_cx_tower_scheduled_task_view_form
+msgid ""
+"\n"
+" "
+msgstr ""
+"\n"
+" "
+
+#. 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.res_config_settings_view_form
+msgid "Files will be pulled from server to Tower automatically using cron job."
+msgstr "I file verranno scaricati dal server a Tower automaticamente usando un lavoro cron."
+
+#. 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 "Compilare le variabili di configurazione richieste nella sezione “Valori configurazione” prima di eseguire il comando."
+
+#. 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 "Scarica i file dal server"
+
+#. 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.res_config_settings_view_form
+msgid "Scheduled tasks will be run automatically using cron job."
+msgstr "I lavori schedulati verranno eseguiti automaticamente usando un lavoro cron."
+
+#. 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
+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 "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 "Partner:"
+
+#. 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
+#. odoo-javascript
+#: code:addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.esm.js:0
+#, python-format
+msgid "Ace Tower Editor"
+msgstr "Editor Ace Tower"
+
+#. 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 addizionali 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 auitomaticamente dall'opzione selezionata. Le modifiche manuali verranno sovrascritte quando si modifica 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 "Non si può cancellare l'etichetta '%(tag_name)s' perché è usata nei record relativi."
+
+#. 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 aiuto 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 "Esecuzione 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_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 "Archivio 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 archivio 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
+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
+msgid "Cetmix Tower: Check zombie commands"
+msgstr "Cetmix Tower: controllo comandi zombi"
+
+#. module: cetmix_tower_server
+#: model:ir.actions.server,name:cetmix_tower_server.ir_cron_run_scheduled_tasks_ir_actions_server
+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 il log %(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 inserire le impostazioni server. Non inserire la chiave se si hanno dei 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 fille 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 "Codice comando"
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_variable__command_ids_count
+msgid "Command Count"
+msgstr "Conteggio comando"
+
+#. 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 "Dominio comando"
+
+#. module: cetmix_tower_server
+#: model:ir.model.fields,field_description:cetmix_tower_server.field_cx_tower_command__command_help
+msgid "Command Help"
+msgstr "Aiuto comando"
+
+#. 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 "Registro comando"
+
+#. 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 "Registri comando"
+
+#. 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 "Timaout comando"
+
+#. 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 "Variabili comando"
+
+#. 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 "Il codice comando che è stato 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 "Il comando è andato in timeout ed è stato 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."
+msgstr "Timeout in secondi del comando dopo i quali il comando verrà terminato. Impostare 0 per disabilitare il timeout."
+
+#. 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 "Comando usato 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à lanciata questa linea del piano di volo. Ad esempio: {{ 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 attuale Cetmix Tower su cui il comando è in esecuzione"
+
+#. 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 personalizzato"
+
+#. 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 personalizzati"
+
+#. 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 fallisce il controllo dello schema"
+
+#. 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 "Valori segreti personalizzati possono essere definiti solo per il tipo chiave 'segreta'"
+
+#. 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 personalizzati"
+
+#. 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 variabile personalizzati per procedura guidata esecuzione comando"
+
+#. 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 variabile personalizzati per procedura guidata esecuzione 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 personalizzati 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 variabile personalizzati 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 personalizzati 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 unico 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 "Abilita il salto della verifica della chiave dell'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 recuperare la 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. Aggiornamento 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 automatically using cron job."
+msgstr "I file verranno scaricati dal server a Tower automaticamente usando un lavoro cron."
+
+#. 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 "Righe 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 "Piano di volo usato"
+
+#. 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 piano di volo usato"
+
+#. 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 del comando attuale. 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 "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 in cui è usato questo comando"
+
+#. 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 "Comparazione virgola mobile. Funzione aiuto Odoo per confronto 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 "Referenza di gruppo %s non trovata!"
+
+#. 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 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 "Ha accesso al server"
+
+#. 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"
+msgstr "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 che voglio inserirla nelle impostazioni 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 "Il file esiste"
+
+#. 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 abilitata, disconnette il file dal suo modello dopo l'esecuzione del 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. Aggiungere sudo all'inizio dell'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. Utilizzato 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 abilitata, istanze multiple dello stesso comando possono essere eseguite sullo stesso server contemporaneamente.\n"
+"Altrimenti, 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 abilitata, istanze multiple dello stesso piano di volo possono essere eseguite sullo stesso server contempraneamente.\n"
+"Altrimenti, 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 sono usate per verificare l'identità dell'host remoto. Accettare le chiavi di un host sconosciuto può lasciare la connessione aperta 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 "Inserisci 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 "Ultima data esecuzione"
+
+#. 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. Si può usare {{ variables }} 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_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à usato 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
+#. odoo-javascript
+#: code:addons/cetmix_tower_server/static/src/components/ace_variables/ace_variables.esm.js:0
+#, python-format
+msgid "Mode"
+msgstr "Modalità"
+
+#. 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 del modello della risorsa che usa 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 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 "Nessun segreto 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 "Nessuna variabile trovata"
+
+#. 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 "SO %(os)s usato dal server '%(srv)s' non è presente nella lista compatibilità del SO 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 "Si può definire solo una un 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 "Si può definire solo un valore segreto per un partner"
+
+#. 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 "Si può definire solo un valore segreto per un server"
+
+#. 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 "Si può definire solo un valore segreto per un server e 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 "Sono visualizzati solo i valori a cui ha accesso l'utente attuale."
+
+#. 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 compatibilità SO"
+
+#. 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 righe 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 "Orario 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.
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. Usare 'new' per creare oggetti HMAC. Metodi disponibili nell'*oggetto* HMAC: 'update', 'copy', 'digest', 'hexdigest'. Module-level function: '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 'json' Python. 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 Pyhon utilizzato per modificare i valori della variabile.\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 stringa 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 "Genera 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_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, es. ^[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 ""
+"Schema regex per validare i valori della variabile usando la funzione 're.match'. Es. ^[a-z0-9]+$ \n"
+"Se vuoto, i valori della variabile non verranno validati."
+
+#. 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
+#. 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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban
+#, 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
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_server_view_kanban
+#, 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 "Esegui 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 "Esegue 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 "Esegui comando 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 comando esecuzione 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 attivato 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 attivato 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 eseguiti 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."
+msgstr "I lavori schedulati verranno eseguiti automaticamente usando un lavoro cron."
+
+#. 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 "Scorciatoie ricerca "
+
+#. 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 "Dati segreti"
+
+#. 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 la 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
+#: 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 "Visualizza 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 "Visualizza 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 "Visualizza chiave SSH"
+
+#. 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 SSH"
+
+#. 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.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 salvare 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 del lavoro selezionato è 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 "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_plan__on_error_action
+msgid "This action will be triggered on error if no command action can be applied"
+msgstr "Questa azione verrà eseguita 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à elaborato usando 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 valore è applicabile a tutti i server e partner"
+
+#. 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 "Timeout in secondi per i comandi dopo i quali il comando verrà terminato"
+
+#. 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 "Scorciatoia attivazione"
+
+#. module: cetmix_tower_server
+#: model_terms:ir.ui.view,arch_db:cetmix_tower_server.cx_tower_command_log_view_form
+msgid "Triggered Commands"
+msgstr "Comandi 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 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 il lavoro schedulato'%(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. Aiuto 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 "Messaggio 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 di compatibilità SO del comando"
+
+#. 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 si è abilitati a modificare l'uscita del log 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 \"Responsabile\" 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 (es. 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 "responsabili 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 "responsabili che possono modificare questo server"
+
+#. 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 "responsabili che possono modificare questo modello"
+
+#. 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: operarioni regex (es. 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à usato come valore della 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 acceder 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 accedere a 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 "value: valore della 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 "quali 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\"."
+#~ "li>\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"
+#~ "a> 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
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"
+#~ "
\n"
+#~ " Ogni comando codice Python restituisce il valore risultato"
+#~ "code> che è un dizionario.\n"
+#~ " Ci sono due chiavi nel dizionario:\n"
+#~ "
\n"
+#~ "
exit_code: Integer. codice di uscita del comando. \"0"
+#~ "\" significa successo, e ogni altro valore significa fallimento. Il "
+#~ "valore predefinito è \"0\".
\n"
+#~ "
message: String. messaggio da registrare. Il valore "
+#~ "predefiito è \"None\".
\n"
+#~ "
\n"
+#~ "Qui un esempio di un codice comando Python:\n"
+#~ "\n"
+#~ " server_name = server.name\n"
+#~ " result = {\"exit_code\": 0, \"message\": \"Il nome del server è \" + "
+#~ "server_name}\n"
+#~ "\n"
+#~ "
hmac: libreria Python 'hmac'. Usare 'new' per creare un "
+#~ "oggetto HMAC. \n"
+#~ " Metodi disponibili nell'*oggetto* HMAC: 'update', 'copy', "
+#~ "'digest', 'hexdigest'. \n"
+#~ " Funzione a livello modulo: 'compare_digest'.
\n"
+#~ "
UserError: eccezione avviso 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 ""
+#~ "# Fare riferimento alla linguetta 'Aiuto' e alla documentazione per "
+#~ "ulteriori informazioni.\n"
+#~ "#\n"
+#~ "# Si può restituire un 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\": None}\n"
+#~ "#\n"
+#~ "# Variabili disponibili:\n"
+#~ "# - user: utente Odoo attuale\n"
+#~ "# - env: Ambiente Odoo nel quale l'azione è attivata\n"
+#~ "# - server: il server sul quale 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'. Usare 'new' per creare aun nuovo "
+#~ "oggetto HMAC.\n"
+#~ "# Metodi disponibili nell *oggetto* HMAC: 'update', 'copy', 'digest', "
+#~ "'hexdigest'.\n"
+#~ "# Funzione livello modulo: 'compare_digest'.\n"
+#~ "# - float_compare: funzione Odoo per confrontare floats in base ad una "
+#~ "precisione specifica\n"
+#~ "# - UserError: eccezione avviso da usare 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 ""
+#~ "# Eseguire qualsiasi comando SSH nel sistema destinazione\n"
+#~ "# Esempi: ls, cd, pwd, mkdir, rm\n"
+#~ "# Adattare i comandi al proprio SO.\n"
+
+#~ 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 ""
+#~ ""
+#~ msgstr ""
+#~ ""
+
+#~ 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 "Main Attachment"
+#~ msgstr "Allegato principale"
+
+#~ 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 ""
+#~ "Run\n"
+#~ " Command"
+#~ msgstr ""
+#~ "Esegui\n"
+#~ " comando"
+
+#~ msgid ""
+#~ "Run\n"
+#~ " Flight Plan"
+#~ msgstr ""
+#~ "Esegui\n"
+#~ " piano di volo"
+
+#~ 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/18.0.3.0.0/pre-migration.py b/addons/cetmix_tower_server/migrations/18.0.3.0.0/pre-migration.py
new file mode 100644
index 0000000..8c387d3
--- /dev/null
+++ b/addons/cetmix_tower_server/migrations/18.0.3.0.0/pre-migration.py
@@ -0,0 +1,17 @@
+# Remove the "unique_variable_value_server" constraint for cx_tower_variable_value model
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+def migrate(cr, version):
+ """Remove the unique_variable_value_server constraint before migration."""
+ cr.execute(
+ """
+ ALTER TABLE cx_tower_variable_value
+ DROP CONSTRAINT IF EXISTS unique_variable_value_server
+ """
+ )
+ _logger.info(
+ "Removed unique_variable_value_server constraint from cx_tower_variable_value"
+ )
diff --git a/addons/cetmix_tower_server/models/__init__.py b/addons/cetmix_tower_server/models/__init__.py
new file mode 100644
index 0000000..7b0739b
--- /dev/null
+++ b/addons/cetmix_tower_server/models/__init__.py
@@ -0,0 +1,52 @@
+# 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_metadata_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
+from . import res_users
+
+# Jets
+from . import cx_tower_jet_template_dependency
+from . import cx_tower_jet_dependency
+from . import cx_tower_jet_state
+from . import cx_tower_jet_action
+from . import cx_tower_jet_template
+from . import cx_tower_jet_template_install
+from . import cx_tower_jet_template_install_line
+from . import cx_tower_jet
+from . import cx_tower_jet_request
+from . import cx_tower_jet_waypoint_template
+from . import cx_tower_jet_waypoint
diff --git a/addons/cetmix_tower_server/models/cetmix_tower.py b/addons/cetmix_tower_server/models/cetmix_tower.py
new file mode 100644
index 0000000..dce5615
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cetmix_tower.py
@@ -0,0 +1,313 @@
+# Copyright (C) 2024 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+import logging
+import time
+import warnings
+
+from odoo import _, api, models
+from odoo.exceptions import ValidationError
+
+from . import tools
+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.
+ """
+
+ _name = "cetmix.tower"
+ _description = "Cetmix Tower Odoo Automation"
+
+ @api.model
+ def server_create_from_template(self, template_reference, server_name, **kwargs):
+ """
+ THIS METHOD IS DEPRECATED. USE THE 'cx.tower.server.template' MODEL DIRECTLY.
+ """
+ _logger.warning(
+ "server_create_from_template: This method is deprecated "
+ "and will be removed in the future. "
+ "Use the 'cx.tower.server.template' model directly instead."
+ )
+ 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
+ ):
+ """
+ THIS METHOD IS DEPRECATED. USE THE 'cx.tower.server' MODEL DIRECTLY.
+ """
+
+ _logger.warning(
+ "server_run_command: This method is deprecated and "
+ "will be removed in the future. "
+ "Use the 'cx.tower.server' model directly instead."
+ )
+ 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
+ ):
+ """THIS METHOD IS DEPRECATED. USE THE 'cx.tower.server' MODEL DIRECTLY."""
+ _logger.warning(
+ "server_run_flight_plan: This method is deprecated and "
+ "will be removed in the future. "
+ "Use the 'cx.tower.server' model directly instead."
+ )
+ 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):
+ """THIS METHOD IS DEPRECATED. USE THE 'cx.tower.server' MODEL DIRECTLY."""
+ _logger.warning(
+ "server_set_variable_value: This method is deprecated and "
+ "will be removed in the future. "
+ "Use the 'cx.tower.server' model directly instead."
+ )
+ 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
+ ):
+ """THIS METHOD IS DEPRECATED. USE THE 'cx.tower.server' MODEL DIRECTLY."""
+ _logger.warning(
+ "server_get_variable_value: This method is deprecated and "
+ "will be removed in the future. "
+ "Use the 'cx.tower.server' model directly instead."
+ )
+ if not check_global:
+ warnings.warn(
+ "server_get_variable_value: 'check_global' is deprecated and "
+ "will be removed in the future. "
+ "Global values are always checked.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
+ # Get server by reference
+ server = self.env["cx.tower.server"].get_by_reference(server_reference)
+ if not server:
+ _logger.warning(
+ "server_get_variable_value: Server not found for reference '%s'",
+ server_reference,
+ )
+ return None
+ return (
+ self.env["cx.tower.variable"]
+ ._get_variable_values_by_references(
+ variable_references=[variable_reference], server=server
+ )
+ .get(variable_reference)
+ )
+
+ @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 uses the `test_ssh_connection` method
+ of the 'cx.tower.server' model.
+ It tries to connect to the server multiple times
+ and is designed to be used in the Python commands or
+ Odoo automated actions.
+
+ 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 simple command for verification.
+ Default is True. Set to False to skip command execution.
+ try_file (bool): Try file operations for verification.
+ Default is True. Set to False to skip file operations.
+ 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
+ ):
+ """
+ Validates 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
+
+ @api.model
+ def generate_random_id(self, sections=1, population=4, separator="-"):
+ """
+ Helper method that allows to generate a random id
+ with customizable sections and population.
+ Such ids are more human readable and less likely to collide.
+
+
+ Args:
+ sections (int): Number of sections to generate.
+ population (int): Population of the sections.
+ separator (str): Separator between sections.
+ Returns:
+ str: Random id
+ """
+ return tools.generate_random_id(
+ sections=sections, population=population, separator=separator
+ )
+
+ @api.model
+ def is_valid_url(self, url, no_scheme_check=False):
+ """
+ Check if the provided URL is a valid URL.
+ The `urlparse` function from the `urllib.parse` module is used.
+
+ Args:
+ url (str): URL to check
+ no_scheme_check (bool): If True, the scheme check will be skipped.
+ Defaults to False.
+ Returns:
+ bool: True if the URL is valid, False otherwise
+ """
+ return tools.is_valid_url(url=url, no_scheme_check=no_scheme_check)
diff --git a/addons/cetmix_tower_server/models/constants.py b/addons/cetmix_tower_server/models/constants.py
new file mode 100644
index 0000000..1eb055f
--- /dev/null
+++ b/addons/cetmix_tower_server/models/constants.py
@@ -0,0 +1,152 @@
+from odoo.tools import LazyTranslate
+
+_lt = LazyTranslate(__name__, default_lang="en_US")
+
+# ***
+# 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 = _lt("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
+
+
+# -- Jet: -500 > -599
+
+# Returned when the jet action is not found
+JET_ACTION_NOT_FOUND = -501
+
+# Returned when the jet template is not found
+JET_TEMPLATE_NOT_FOUND = -502
+
+# Returned when the jet is not found
+JET_NOT_FOUND = -503
+
+# Returned when a jet state error occurs
+JET_STATE_ERROR = -504
+
+# Returned when the jet action is not available
+JET_ACTION_NOT_AVAILABLE = -505
+
+# Returned when the jet dependencies are not satisfied
+JET_DEPENDENCIES_NOT_SATISFIED = -506
+
+# Returned when the waypoint template is not found or not set
+WAYPOINT_TEMPLATE_NOT_FOUND = -507
+
+# Returned when waypoint creation fails (e.g. template not for jet, jet busy)
+WAYPOINT_CREATE_FAILED = -508
+
+
+# -- Default values
+
+# Default Python code used in Python code command
+DEFAULT_PYTHON_CODE = _lt("""
+# 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 = _lt("""
+
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
new file mode 100644
index 0000000..c2d50db
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_access_mixin.py
@@ -0,0 +1,37 @@
+# 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
new file mode 100644
index 0000000..37c3b9e
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_access_role_mixin.py
@@ -0,0 +1,99 @@
+# 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
new file mode 100644
index 0000000..32469af
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_command.py
@@ -0,0 +1,657 @@
+# Copyright (C) 2022 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+import logging
+from types import SimpleNamespace
+from urllib import parse
+
+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_context
+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
+
+_logger = logging.getLogger(__name__)
+
+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",
+ ],
+)
+re = wrap_module(
+ __import__("re"),
+ [
+ "match",
+ "fullmatch",
+ "search",
+ "sub",
+ "subn",
+ "split",
+ "findall",
+ "finditer",
+ "compile",
+ "template",
+ "escape",
+ "error",
+ ],
+)
+hmac = wrap_module(
+ __import__("hmac"),
+ ["new", "compare_digest"],
+)
+urllib_parse = wrap_module(
+ parse,
+ [
+ "urlparse",
+ "urljoin",
+ "urlunparse",
+ "urlencode",
+ "urlsplit",
+ "urlunsplit",
+ "parse_qs",
+ "parse_qsl",
+ "quote",
+ "quote_plus",
+ "quote_from_bytes",
+ "unquote",
+ "unquote_plus",
+ "unquote_to_bytes",
+ ],
+)
+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",
+ 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"
+ ),
+ )
+ # -- Jets
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ help="Action will be triggered for all dependent jets" " of this template",
+ )
+ jet_action_id = fields.Many2one(
+ comodel_name="cx.tower.jet.action",
+ help="Action to trigger",
+ domain="[('jet_template_id', '=', jet_template_id)]",
+ )
+ # -- Waypoints
+ waypoint_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.waypoint.template",
+ string="Waypoint Template",
+ help="Waypoint template to create the waypoint from. Used when action is "
+ "Create a Waypoint.",
+ )
+ fly_here = fields.Boolean(
+ default=False,
+ help="When enabled, the created waypoint is set as current (fly to) "
+ "after creation.",
+ )
+
+ # ---- 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", "Python Code"),
+ ("file_using_template", "Create/Update File"),
+ ("plan", "Run Flight Plan"),
+ ("jet_action", "Trigger Jet Action"),
+ ("create_waypoint", "Create Waypoint"),
+ ]
+
+ # -- 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 command log records
+ """
+ self.ensure_one()
+ 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_context(keys=("lang",))
+ @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 = {
+ "_logger": {
+ "import": _logger,
+ "help": _(
+ "Logger object. Use with caution! Only for debugging purposes."
+ ),
+ },
+ "re": {
+ "import": re,
+ "help": _("Python 're' library for regex operations"),
+ },
+ "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'"
+ ),
+ },
+ "urllib_parse": {
+ "import": urllib_parse,
+ "help": _("Python 'urllib.parse' library methods."),
+ },
+ "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.
"
+ ),
+ },
+ }
+ 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, jet_template=None, jet=None, waypoint=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.
+ jet_template: Jet template to get the Odoo objects for.
+ jet: Jet to get the Odoo objects for.
+ waypoint: Waypoint 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"),
+ },
+ "jet_template": {
+ "import": jet_template,
+ "help": _(
+ "Current Cetmix Tower jet template this command is running on"
+ ),
+ },
+ "jet": {
+ "import": jet,
+ "help": _("Current Cetmix Tower jet this command is running on"),
+ },
+ "waypoint": {
+ "import": waypoint,
+ "help": _(
+ "Current Cetmix Tower Jet waypoint 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_jets": {
+ "import": self.env["cx.tower.jet"],
+ "help": _("A helper shortcut to env['cx.tower.jet']"),
+ },
+ "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']"),
+ },
+ "tower_waypoints": {
+ "import": self.env["cx.tower.jet.waypoint"],
+ "help": _(
+ "A helper shortcut to env['cx.tower.jet.waypoint']"
+ ),
+ },
+ }
+
+ 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.
+
+ : The library object to expose.
+ : Help text (HTML) shown in the "Help" tab.
+ """
+ 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.
+ **kwargs: Additional keyword arguments.
+ Returns:
+ dict: Evaluation context for the python command.
+ """
+
+ # Get the jet template, jet and waypoint from kwargs
+ jet_template = kwargs.get("jet_template")
+ jet = kwargs.get("jet")
+ waypoint = kwargs.get("waypoint")
+
+ # Get the Odoo objects first
+ imports = self._get_python_command_odoo_objects(
+ server=server,
+ jet_template=jet_template,
+ jet=jet,
+ waypoint=waypoint,
+ )
+
+ # 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") or {}
+ return eval_context
+
+ def _get_banned_python_code_keywords(self):
+ """
+ Get the banned python code keywords for the python command.
+ Extend this method to add banned keywords to the list.
+
+ Returns:
+ list: Banned python code keywords.
+ """
+ return ["_set_secret_values(", "_get_secret_value(", "_get_secret_values("]
diff --git a/addons/cetmix_tower_server/models/cx_tower_command_log.py b/addons/cetmix_tower_server/models/cx_tower_command_log.py
new file mode 100644
index 0000000..aa9d974
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_command_log.py
@@ -0,0 +1,400 @@
+# 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",
+ )
+ server_id = fields.Many2one(
+ comodel_name="cx.tower.server", required=True, index=True, ondelete="cascade"
+ )
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ index=True,
+ ondelete="cascade",
+ compute="_compute_jet_id",
+ store=True,
+ readonly=False,
+ )
+ jet_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ index=True,
+ ondelete="cascade",
+ compute="_compute_jet_id",
+ store=True,
+ readonly=False,
+ )
+ waypoint_id = fields.Many2one(
+ comodel_name="cx.tower.jet.waypoint",
+ related="plan_log_id.waypoint_id",
+ readonly=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",
+ 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("server_id.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("plan_log_id")
+ def _compute_jet_id(self):
+ for command_log in self:
+ if command_log.plan_log_id:
+ command_log.update(
+ {
+ "jet_id": command_log.plan_log_id.jet_id,
+ "jet_template_id": command_log.plan_log_id.jet_template_id,
+ }
+ )
+
+ @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
new file mode 100644
index 0000000..325bca4
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_custom_variable_value_mixin.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2025 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+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
new file mode 100644
index 0000000..faaf7d8
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_file.py
@@ -0,0 +1,783 @@
+# 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",
+ index=True,
+ ondelete="cascade",
+ compute="_compute_server_id",
+ store=True,
+ readonly=False,
+ )
+ 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",
+ )
+
+ # Jets
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ help="Jet template this file belongs to",
+ index=True,
+ compute="_compute_server_id",
+ store=True,
+ readonly=False,
+ )
+ jet_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ help="Jet this file belongs to",
+ index=True,
+ )
+
+ @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("jet_id", "jet_id.server_id", "jet_id.jet_template_id")
+ def _compute_server_id(self):
+ for record in self:
+ if record.jet_id:
+ record.update(
+ {
+ "server_id": record.jet_id.server_id,
+ "jet_template_id": record.jet_id.jet_template_id,
+ }
+ )
+ else:
+ # Reset the jet template id if the jet is removed
+ if record.jet_template_id:
+ record.jet_template_id = False
+
+ @api.depends("server_id", "template_id", "name", "server_dir", "code")
+ def _compute_render(self):
+ """
+ Compute file name, directory and code
+ """
+ variable_obj = self.env["cx.tower.variable"]
+ 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
+
+ # Get variable values for the server the file is linked to
+ var_vals = variable_obj._get_variable_values_by_references(
+ variables,
+ server=file.server_id,
+ jet_template=file.jet_template_id,
+ jet=file.jet_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 = file.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.get("server_dir") or "").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("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 %(action)s %(f)s to/from server: %(err)s",
+ action=action,
+ 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 and file.auto_sync_interval:
+ 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
new file mode 100644
index 0000000..bcd52d1
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_file_template.py
@@ -0,0 +1,247 @@
+# 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", jet_template=None, jet=None
+ ):
+ """
+ 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.
+ :param jet_template: cx.tower.jet.template, optional
+ The jet template to use for creating the new file.
+ :param jet: cx.tower.jet, optional
+ The jet to use for creating the new file.
+
+ :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(
+ _(
+ "Invalid if_file_exists value: %(if_file_exists)s. "
+ "Expected one of %(valid_behaviors)s.",
+ if_file_exists=if_file_exists,
+ valid_behaviors=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 = self.env["cx.tower.variable"]._get_variable_values_by_references(
+ variables,
+ server=server,
+ jet_template=jet_template,
+ jet=jet,
+ )
+
+ 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, # pylint: disable=no-member
+ "jet_template_id": jet_template.id if jet_template else None,
+ "jet_id": jet.id if jet else None,
+ }
+ )
+ return existing_file
+
+ vals = {
+ "name": self.file_name,
+ "server_id": server.id,
+ "server_dir": existing_dir,
+ "template_id": self.id, # pylint: disable=no-member
+ "code": self.code,
+ "file_type": self.file_type,
+ "source": self.source,
+ "auto_sync": self.auto_sync,
+ "jet_template_id": jet_template.id if jet_template else None,
+ "jet_id": jet.id if jet else None,
+ }
+
+ 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_jet.py b/addons/cetmix_tower_server/models/cx_tower_jet.py
new file mode 100644
index 0000000..0be05bb
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet.py
@@ -0,0 +1,1703 @@
+# Copyright (C) 2024 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+import ast
+import logging
+
+from odoo import _, api, fields, models
+from odoo.exceptions import AccessError, ValidationError
+
+from .constants import (
+ JET_ACTION_NOT_AVAILABLE,
+ JET_DEPENDENCIES_NOT_SATISFIED,
+ JET_STATE_ERROR,
+)
+from .tools import generate_random_id
+
+_logger = logging.getLogger(__name__)
+
+
+class CxTowerJet(models.Model):
+ """Jets represent application instances that can be managed independently"""
+
+ _name = "cx.tower.jet"
+ _description = "Cetmix Tower Jet"
+ _inherit = [
+ "cx.tower.reference.mixin",
+ "cx.tower.variable.mixin",
+ "cx.tower.metadata.mixin",
+ "mail.thread",
+ "mail.activity.mixin",
+ "cx.tower.tag.mixin",
+ "cx.tower.access.role.mixin",
+ ]
+ _order = "sequence, name"
+ _mail_post_access = "read"
+
+ active = fields.Boolean(default=True)
+ deletable = fields.Boolean(
+ readonly=True,
+ default=True,
+ help="This field is set by the jet actions. "
+ "If enabled, the jet can be deleted",
+ )
+ url = fields.Char(string="URL", help="Jet URL, eg 'https://meme.example.com'")
+ color = fields.Integer(related="state_id.color", readonly=True)
+ icon = fields.Image(
+ string="Icon image",
+ related="jet_template_id.icon",
+ readonly=True,
+ store=False,
+ help="Jet icon, computed from the template by default",
+ )
+ sequence = fields.Integer(default=10, help="Used to sort jets in views")
+ partner_id = fields.Many2one(
+ comodel_name="res.partner",
+ help="Partner associated with this jet",
+ )
+ note = fields.Text()
+
+ jet_cloned_from_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ string="Cloned from",
+ readonly=True,
+ copy=False,
+ help="Jet this jet was cloned from. "
+ "This field is set when the jet is cloned from another jet.",
+ )
+
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ required=True,
+ ondelete="restrict",
+ help="Template that this jet is based on",
+ )
+ jet_template_domain = fields.Binary(
+ compute="_compute_jet_template_domain",
+ )
+ server_id = fields.Many2one(
+ comodel_name="cx.tower.server",
+ required=True,
+ ondelete="restrict",
+ help="Server where this jet is running",
+ )
+ server_allowed_domain = fields.Binary(
+ compute="_compute_server_allowed_domain",
+ )
+ file_ids = fields.One2many(
+ comodel_name="cx.tower.file",
+ inverse_name="jet_id",
+ string="Files",
+ help="Files of this jet",
+ )
+ server_log_ids = fields.One2many(
+ comodel_name="cx.tower.server.log",
+ inverse_name="jet_id",
+ copy=False,
+ )
+ scheduled_task_ids = fields.Many2many(
+ comodel_name="cx.tower.scheduled.task",
+ relation="cx_tower_scheduled_task_jet_rel",
+ column1="jet_id",
+ column2="scheduled_task_id",
+ string="Scheduled Tasks",
+ )
+
+ # -- Jet Requests
+ served_jet_request_id = fields.Many2one(
+ comodel_name="cx.tower.jet.request",
+ help="Request this jet is currently serving",
+ readonly=True,
+ copy=False,
+ )
+
+ # -- Dependencies
+ jet_requires_ids = fields.One2many(
+ comodel_name="cx.tower.jet.dependency",
+ inverse_name="jet_id",
+ string="Requires",
+ help="Other jets this jet depends on",
+ compute="_compute_jet_requires_ids",
+ store=True,
+ groups="cetmix_tower_server.group_manager",
+ copy=False,
+ )
+ jet_required_by_ids = fields.One2many(
+ comodel_name="cx.tower.jet.dependency",
+ inverse_name="jet_depends_on_id",
+ string="Required By",
+ help="Jets that depend on this jet",
+ groups="cetmix_tower_server.group_manager",
+ copy=False,
+ readonly=True,
+ )
+
+ # -- States and actions
+ state_id = fields.Many2one(
+ comodel_name="cx.tower.jet.state",
+ string="Current State",
+ tracking=True,
+ domain="[('id', 'in', jet_template_state_ids)]",
+ copy=False,
+ )
+ state = fields.Char(
+ related="state_id.reference",
+ readonly=True,
+ store=True,
+ index=True,
+ string="State Reference",
+ help="Current state of the jet. "
+ "NB: this is "
+ "the reference of the state, not the name.",
+ )
+ jet_template_state_ids = fields.One2many(
+ comodel_name="cx.tower.jet.state",
+ compute="_compute_state_available_ids",
+ )
+ state_available_ids = fields.One2many(
+ comodel_name="cx.tower.jet.state",
+ compute="_compute_state_available_ids",
+ help="Available states for the jet. "
+ "Click on the button to transition to the state.",
+ copy=False,
+ )
+
+ target_state_id = fields.Many2one(
+ comodel_name="cx.tower.jet.state",
+ string="Target State",
+ readonly=True,
+ copy=False,
+ help="Destination state to which the jet is currently transitioning",
+ )
+ show_available_states = fields.Boolean(
+ help="Show available states in the jet view",
+ compute="_compute_show_available_states",
+ inverse="_inverse_show_available_states",
+ groups="cetmix_tower_server.group_manager",
+ )
+ action_available_ids = fields.Many2many(
+ comodel_name="cx.tower.jet.action",
+ compute="_compute_available_actions",
+ string="Available Actions",
+ help="Available actions for the jet. "
+ "Click on the button to trigger the action.",
+ )
+ current_action_id = fields.Many2one(
+ comodel_name="cx.tower.jet.action",
+ string="Executing Action",
+ readonly=True,
+ copy=False,
+ )
+ current_command_log_id = fields.Many2one(
+ comodel_name="cx.tower.command.log",
+ string="Executing Command Log",
+ groups="cetmix_tower_server.group_manager",
+ readonly=True,
+ copy=False,
+ )
+
+ # -- Waypoints
+ is_waypoints_available = fields.Boolean(
+ compute="_compute_is_waypoints_available",
+ readonly=True,
+ )
+ waypoint_ids = fields.One2many(
+ comodel_name="cx.tower.jet.waypoint",
+ inverse_name="jet_id",
+ string="Waypoints",
+ help="Waypoints of the jet",
+ copy=False,
+ )
+ waypoint_id = fields.Many2one(
+ comodel_name="cx.tower.jet.waypoint",
+ help="Current waypoint of the jet",
+ readonly=True,
+ copy=False,
+ tracking=True,
+ )
+
+ # -- Variables used for configuration
+ variable_value_ids = fields.One2many(
+ inverse_name="jet_id",
+ )
+
+ # -- Logs
+ command_log_ids = fields.One2many(
+ comodel_name="cx.tower.command.log",
+ inverse_name="jet_id",
+ copy=False,
+ )
+ plan_log_ids = fields.One2many(
+ comodel_name="cx.tower.plan.log",
+ inverse_name="jet_id",
+ copy=False,
+ )
+
+ # -- Access. Add relation for mixin fields
+ user_ids = fields.Many2many(
+ relation="cx_tower_jet_user_rel",
+ )
+ manager_ids = fields.Many2many(
+ relation="cx_tower_jet_manager_rel",
+ )
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Compute methods
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @api.depends("name", "state_id")
+ def _compute_display_name(self):
+ """Compute the display name of the jet"""
+ for jet in self:
+ jet.display_name = f"{jet.name} ({jet.state})" if jet.state else jet.name
+
+ @api.depends("server_id")
+ def _compute_jet_template_domain(self):
+ """Compute the domain of the jet template"""
+ for jet in self:
+ jet.jet_template_domain = (
+ [("server_ids", "in", [jet.server_id.id])] if jet.server_id else []
+ )
+
+ @api.depends("jet_template_id")
+ def _compute_server_allowed_domain(self):
+ """Compute the domain of the server allowed"""
+ for jet in self:
+ jet.server_allowed_domain = (
+ [("id", "in", jet.jet_template_id.server_ids.ids)]
+ if jet.jet_template_id and jet.jet_template_id.server_ids
+ else []
+ )
+
+ @api.depends("jet_template_id", "jet_template_id.action_ids")
+ def _compute_state_available_ids(self):
+ """Compute the available states for the jet"""
+ for jet in self:
+ if not jet.jet_template_id:
+ jet.update(
+ {
+ "jet_template_state_ids": False,
+ "state_available_ids": False,
+ }
+ )
+ continue
+ actions = jet.jet_template_id.action_ids
+ if not actions:
+ jet.update(
+ {
+ "jet_template_state_ids": False,
+ "state_available_ids": False,
+ }
+ )
+ continue
+ # Compute effective access level for the user
+ effective_user_access_level = jet._get_user_effective_access_level()
+ jet.update(
+ {
+ "jet_template_state_ids": actions.state_from_id
+ | actions.state_transit_id
+ | actions.state_to_id,
+ "state_available_ids": (
+ actions.state_to_id - jet.state_id
+ ).filtered(
+ lambda s,
+ access_level=effective_user_access_level: s.access_level
+ <= access_level
+ ),
+ }
+ )
+
+ @api.depends(
+ "state_id",
+ "jet_template_id",
+ "jet_template_id.action_ids",
+ "jet_template_id.action_ids.state_from_id",
+ "jet_template_id.action_ids.state_to_id",
+ "jet_template_id.action_ids.priority",
+ )
+ def _compute_available_actions(self):
+ """Compute available actions based on current state and template"""
+ for jet in self:
+ if not jet.jet_template_id:
+ jet.action_available_ids = False
+ continue
+
+ # Find actions in the template that start from the current state
+ actions = jet.jet_template_id.action_ids.filtered(
+ lambda a, state=jet.state_id: a.state_from_id == state
+ )
+ jet.update({"action_available_ids": actions})
+
+ @api.depends("jet_template_id", "jet_template_id.template_requires_ids")
+ def _compute_jet_requires_ids(self):
+ """Compute the dependencies of the jets"""
+ for jet in self:
+ jet_template_dependencies = jet.jet_template_id.template_requires_ids
+
+ final_vals = []
+
+ # 1. Check removed dependencies
+ if jet_template_dependencies:
+ jet_dependencies_to_remove = jet.jet_requires_ids.filtered(
+ lambda d,
+ jtd=jet_template_dependencies: d.jet_template_dependency_id
+ not in jtd
+ )
+ else:
+ jet_dependencies_to_remove = jet.jet_requires_ids
+
+ if jet_dependencies_to_remove:
+ final_vals = [(3, dep.id) for dep in jet_dependencies_to_remove]
+
+ # Check new template dependencies
+ if jet_template_dependencies:
+ if jet.jet_requires_ids:
+ new_jet_template_dependencies = jet_template_dependencies.filtered(
+ lambda d, j=jet: d.id
+ not in j.jet_requires_ids.jet_template_dependency_id.ids
+ )
+ else:
+ new_jet_template_dependencies = jet_template_dependencies
+ for dep in new_jet_template_dependencies:
+ final_vals.append(
+ (
+ 0,
+ 0,
+ {
+ "jet_id": jet.id,
+ "jet_template_dependency_id": dep.id,
+ },
+ )
+ )
+ if final_vals:
+ jet.jet_requires_ids = final_vals
+
+ @api.depends_context("uid")
+ def _compute_show_available_states(self):
+ """Compute if available states should be shown for the jet"""
+ # Set all records at once to avoid multiple writes
+ self.show_available_states = (
+ self.env.user.cetmix_tower_show_jet_available_states
+ )
+
+ def _inverse_show_available_states(self):
+ """Inverse the show available states for the jet"""
+ for jet in self:
+ if jet.show_available_states is not None:
+ jet.env.user.cetmix_tower_show_jet_available_states = (
+ jet.show_available_states
+ )
+
+ @api.depends("jet_template_id", "jet_template_id.waypoint_template_ids")
+ def _compute_is_waypoints_available(self):
+ """Compute if waypoints are available for the jet"""
+ for jet in self:
+ jet.is_waypoints_available = bool(jet.jet_template_id.waypoint_template_ids)
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Constraints
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @api.constrains("server_id", "jet_template_id")
+ def _check_jet_limit_per_server(self):
+ """Check if the jet limit per server is reached"""
+ for jet in self:
+ if (
+ jet.jet_template_id.limit_per_server
+ and jet.jet_template_id.limit_per_server > 0
+ ):
+ if jet.jet_template_id.limit_per_server < len(
+ jet.jet_template_id.jet_ids.filtered(
+ lambda j, s=jet.server_id: j.server_id == s
+ )
+ ):
+ raise ValidationError(
+ _(
+ "Jet limit per server reached for"
+ " '%(jet)s' on server '%(server)s'!",
+ jet=jet.display_name,
+ server=jet.server_id.display_name,
+ )
+ )
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # ORM methods
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @api.model_create_multi
+ def create(self, vals_list):
+ """
+ Create jets
+ - Generate jet reference if not provided
+ """
+
+ for vals in vals_list:
+ if not vals.get("reference"):
+ vals["reference"] = generate_random_id(
+ sections=3, population=4, separator="_"
+ )
+ jets = super().create(vals_list)
+
+ # Create server logs and scheduled tasks
+ for jet in jets:
+ # Server logs
+ for server_log in jet.jet_template_id.server_log_ids:
+ jet_log = server_log.copy(
+ {
+ "jet_id": jet.id,
+ "server_id": jet.server_id.id,
+ "jet_template_id": False,
+ }
+ )
+ if server_log.log_type == "file":
+ jet_log.file_id = server_log.file_template_id.create_file(
+ server=jet.server_id, jet=jet, if_file_exists="skip"
+ ).id
+
+ # Scheduled tasks
+ jet.scheduled_task_ids = jet.jet_template_id.scheduled_task_ids
+
+ return jets
+
+ def write(self, vals):
+ """Handle the entry into the new state"""
+ # Allow modifications in install mode only to load demo data
+ if ("jet_template_id" in vals or "server_id" in vals) and not (
+ self._context.get("install_mode") and self._context.get("install_xmlid")
+ ):
+ raise ValidationError(
+ _(
+ "Jet template and server cannot be changed"
+ " once the jet is created!"
+ )
+ )
+ if "state_id" in vals:
+ for jet in self:
+ jet._on_state_exit(state=jet.state_id)
+ res = super().write(vals)
+ for jet in self:
+ jet._on_state_enter(state=jet.state_id)
+ else:
+ res = super().write(vals)
+ return res
+
+ def unlink(self):
+ """
+ Unlink all related files
+ """
+
+ # Check if the jet is deletable
+ not_deletable_jets = self.filtered(lambda j: not j.deletable)
+ if not_deletable_jets:
+ raise ValidationError(
+ _(
+ "Following jets cannot be deleted as they are not deletable: %s",
+ not_deletable_jets.mapped("display_name"),
+ )
+ )
+ files = self.file_ids
+ res = super().unlink()
+
+ # Unlink files only after the records are deleted
+ # This is done to avoid deleting the files while
+ # the 'unlink' method fails due to some reason.
+ if files:
+ files.unlink()
+ return res
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Odoo Actions
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def action_run_command(self):
+ """
+ Returns wizard action to select command and run it
+ """
+ context = self.env.context.copy()
+ context["default_jet_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["default_jet_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_command_logs(self):
+ """
+ Open current server command log records
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.action_cx_tower_command_log"
+ )
+ action["domain"] = [("jet_id", "=", self.id)] # pylint: disable=no-member
+ return action
+
+ def action_open_plan_logs(self):
+ """
+ Open current server flightplan log records
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.action_cx_tower_plan_log"
+ )
+ action["domain"] = [("jet_id", "=", self.id)] # pylint: disable=no-member
+ return action
+
+ def action_open_state_wizard(self):
+ """Open the jet state wizard"""
+ context = self.env.context.copy()
+ context["default_jet_ids"] = [(6, 0, self.ids)]
+ action = {
+ "type": "ir.actions.act_window",
+ "res_model": "cx.tower.jet.state.wizard",
+ "view_mode": "form",
+ "target": "new",
+ "context": context,
+ }
+ return action
+
+ def action_open_action_wizard(self):
+ """Open the jet action wizard"""
+ context = self.env.context.copy()
+ context["default_jet_ids"] = [(6, 0, self.ids)]
+ action = {
+ "type": "ir.actions.act_window",
+ "res_model": "cx.tower.jet.action.wizard",
+ "view_mode": "form",
+ "target": "new",
+ "context": context,
+ }
+ return action
+
+ def action_open_files(self):
+ """
+ Open files of the current server
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_file_action"
+ )
+ action["domain"] = [("jet_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", {}))
+
+ # Remove group_by from context
+ context.pop("group_by", None)
+ context.update(
+ {
+ "default_jet_id": self.id,
+ "default_server_id": self.server_id.id,
+ }
+ )
+ action["context"] = context
+ return action
+
+ def action_open_requires_jets(self):
+ """
+ Open required jets of the current jet
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_action"
+ )
+ action["domain"] = [("jet_required_by_ids.jet_id", "=", self.id)] # pylint: disable=no-member
+ return action
+
+ def action_open_required_by_jets(self):
+ """
+ Open dependant jets of the current jet
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_action"
+ )
+ action["domain"] = [("jet_requires_ids.jet_depends_on_id", "=", self.id)] # pylint: disable=no-member
+ return action
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # General functions
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def _get_user_effective_access_level(self):
+ """
+ Get the effective access level for the current user.
+ If user is manager but is not added as a manager to the jet,
+ his access level is considered as user.
+ Returns:
+ str: The effective access level for the current user.
+ see _selection_access_level() in cx.tower.access.mixin
+ """
+ self.ensure_one()
+ user_access_level = self.env.user._cetmix_tower_access_level()
+ if user_access_level == "2" and self.env.user not in self.manager_ids:
+ return "1"
+ return user_access_level
+
+ def get_variable_value(self, variable_reference, no_fallback=False):
+ """
+ Return the value of a variable for the current jet.
+ NB: this function follows the value application order.
+ Jet->Jet Template->Server->Global
+
+ Args:
+ variable_reference (Char): The reference of the variable
+ to get the value for
+ no_fallback (bool): If True, will return current record value
+ without checking fallback values.
+
+ Returns:
+ str: The value of the variable for the current record or None
+ """
+ self.ensure_one()
+ if no_fallback:
+ return super().get_variable_value(variable_reference, no_fallback)
+ variable = self.env["cx.tower.variable"].get_by_reference(variable_reference)
+ if not variable:
+ return None
+ values = variable._get_variable_values_by_references(
+ variable_references=[variable_reference], jet=self
+ )
+ return values[variable_reference]
+
+ def run_command(
+ self,
+ command,
+ path=None,
+ sudo=None,
+ ssh_connection=None,
+ **kwargs,
+ ):
+ """Run command on selected Jet.
+ A helper function that calls the corresponding server function.
+
+ Important: this method raises an exception if the jet
+ is currently executing an action.
+ You should handle this exception in your code.
+
+ 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. Returns:
+
+ Returns:
+ dict(): command running result if `no_command_log`
+ context value == True else None
+ """
+ self.ensure_one()
+
+ # Raise an exception if jets is currently executing an action
+ if self.current_action_id:
+ raise ValidationError(
+ _(
+ "Jet '%(jet)s' is currently executing an action",
+ jet=self.display_name,
+ )
+ )
+
+ return self.server_id.run_command(
+ command=command,
+ path=path,
+ sudo=sudo,
+ ssh_connection=ssh_connection,
+ jet=self,
+ **kwargs,
+ )
+
+ def run_flight_plan(self, flight_plan, jet_template=None, **kwargs):
+ """
+ Runs flight plan on the current jet.
+
+ Important: this method raises an exception if the jet
+ is currently executing an action.
+ You should handle this exception in your code.
+
+ Args:
+ flight_plan (cx.tower.plan()): flight plan to run
+ jet_template (cx.tower.jet.template()): jet template
+ to run the flight plan on
+ 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.
+ Raises:
+ ValidationError: If the jet is currently executing an action.
+ Returns:
+ log_record (cx.tower.plan.log()): plan log record
+ """
+
+ self.ensure_one()
+
+ # Raise an exception if jets is currently executing an action
+ # TODO: keep an eye on this method in case we use it
+ # directly in actions.
+ if self.current_action_id:
+ raise ValidationError(
+ _(
+ "Jet '%(jet)s' is currently executing an action",
+ jet=self.display_name,
+ )
+ )
+
+ return self.server_id.run_flight_plan(
+ flight_plan=flight_plan,
+ jet_template=jet_template,
+ jet=self,
+ **kwargs,
+ )
+
+ def bring_to_state(self, state_reference):
+ """
+ Bring the jet to a specific state.
+ This is a wrapper around the _bring_to_state method meant to be used
+ in various automatic actions.
+
+ IMPORTANT: alway prefer using this method over the _bring_to_state method
+ in automation (eg Python commands) because it will check the access level
+ of the user to the state and raise an exception if the user is not allowed
+ to set the state.
+
+ Use `_bring_to_state` method directly if you want to provide a state
+ object instead of a reference.
+
+ Args:
+ state_reference (Char): The reference of the state to bring the jet to.
+ Returns:
+ The jet is brought into the target state.
+ In case of an error, the jet is brought into the error state
+ if the latter is defined.
+
+ Raises:
+ ValidationError: If the state is not found.
+ AccessError: If the user is not allowed to set the state.
+ """
+ self.ensure_one()
+ state = self.env["cx.tower.jet.state"].get_by_reference(state_reference)
+ if not state:
+ raise ValidationError(
+ _(
+ "State '%(state)s' not found for jet '%(jet)s'",
+ state=state_reference,
+ jet=self.display_name,
+ )
+ )
+
+ if state.access_level > self._get_user_effective_access_level():
+ raise AccessError(
+ _("You are not allowed to set the '%(state)s' state!", state=state.name)
+ )
+
+ self._bring_to_state(state)
+
+ def clone(self, server=None, name=None, state=None, **kwargs):
+ """
+ Create a new jet from this template on the given server.
+
+ Following configuration variables will be available in the flight plan:
+ `__original_jet__`: The reference of the original jet
+ `__requested_state__`: The reference of the requested state
+ the new jet was requested to be in.
+
+ Use these variables in the flight plan to identify the original jet
+ and the requested state.
+
+ Args:
+ server (cx.tower.server()): The server to clone the jet on.
+ If not provided, the jet will be cloned on the same server.
+ name (str): The name of the new jet.
+ If not provided, a random name will be generated.
+ state (cx.tower.jet.state()): The state to bring the new jet to.
+
+ Kwargs:
+ field values to populate in the new jet record.
+ NB: configuration variables are provided as follows:
+ (dict): Custom configuration variables
+ Following format is used:
+ `variable_reference`: `variable_value_char`
+ eg:
+ {'branch': 'prod', 'odoo_version': '16.0'}
+ Returns:
+ cx.tower.jet(): The new jet or False if the cloning has failed
+ """
+ self.ensure_one()
+
+ jet_template = self.jet_template_id
+ if not server:
+ server = self.server_id
+ same_server = True
+ else:
+ same_server = server.id == self.server_id.id
+
+ # Check if template allows cloning on the same server
+ if same_server and not jet_template.plan_clone_same_server_id:
+ raise ValidationError(
+ _(
+ "Cloning on the same server is not allowed"
+ " for template '%(template)s'",
+ template=jet_template.name,
+ )
+ )
+ # Check if template allows cloning to a different server
+ if not same_server and not jet_template.plan_clone_different_server_id:
+ raise ValidationError(
+ _(
+ "Cloning to a different server is not allowed"
+ " for template '%(template)s'",
+ template=jet_template.name,
+ )
+ )
+ # Check if the jet creation is allowed on the given server
+ if not jet_template._allow_jet_creation(server):
+ return False
+
+ # Prepare the jet custom values
+ kwargs.update(
+ {
+ "jet_cloned_from_id": self.id,
+ }
+ )
+
+ # Create a new jet
+ jet = jet_template.create_jet(
+ server, name=name or self._default_cloned_jet_name(), **kwargs
+ )
+
+ # Set scheduled tasks of the original jet to the new jet
+ jet.scheduled_task_ids = self.scheduled_task_ids
+
+ # Set server logs of the original jet to the new jet
+ # Delete the server logs of the new jet if the original jet
+ # has no server logs
+ if self.server_log_ids:
+ jet.server_log_ids = [
+ log.copy({"jet_id": False, "server_id": False}).id
+ for log in self.server_log_ids
+ ]
+ # Create files for file-type server logs
+ for jet_log in jet.server_log_ids:
+ if jet_log.log_type == "command":
+ continue
+ if jet_log.log_type == "file":
+ jet_log.file_id = jet_log.file_template_id.create_file(
+ server=jet.server_id, jet=jet, if_file_exists="skip"
+ ).id
+ else:
+ jet.server_log_ids.unlink()
+
+ # NB: we are not passing the state as we need to run
+ # the clone flight plan first.
+ # The plan should take care of the state transition
+ # using the configuration variables.
+ # Update the custom values in the kwargs
+
+ variable_values = {
+ "__original_jet__": self.reference,
+ "__original_server__": self.server_id.reference,
+ "__requested_jet_state__": state.reference if state else None,
+ }
+
+ if same_server and jet_template.plan_clone_same_server_id:
+ jet.run_flight_plan(
+ jet_template.plan_clone_same_server_id, variable_values=variable_values
+ )
+ elif not same_server and jet_template.plan_clone_different_server_id:
+ jet.run_flight_plan(
+ jet_template.plan_clone_different_server_id,
+ variable_values=variable_values,
+ )
+
+ return jet
+
+ def _default_cloned_jet_name(self):
+ """Return default cloned jet name"""
+ self.ensure_one()
+ return f"{self.name} (clone)"
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Jet actions, state transitions, jet requests
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def _trigger_action(
+ self, action, from_transition=False, raise_if_not_available=True, **kwargs
+ ):
+ """Trigger an action on the jet.
+
+ The function flow is:
+
+ 1. Bring the jet into the transit state.
+ 2. Execute the flight plan if defined.
+ 3. Bring the jet into the target state.
+
+ Success:
+ The jet is brought into the target state.
+
+ Error:
+ The jet is brought into the error state if it is defined.
+ Otherwise, the jet is brought into the initial state.
+
+
+ Args:
+ action (cx.tower.jet.action()): The action to trigger
+ from_transition (bool): True if the action is triggered
+ from a transition.
+ This is used to distinguish between a user directly
+ triggering the action and a transition from one state
+ to another.
+ raise_if_not_available (bool):
+ True if the function should raise an exception
+ if the action is not available for this jet.
+ **kwargs: Additional arguments:
+ - current_command_log: Optional command log record to track execution
+
+ Returns:
+ dict: A dictionary with the following keys:
+ - status: The status of the action
+ - error: The error message if the action is not available
+
+ Raises:
+ ValidationError: If the action is not available for this jet.
+ """
+ self.ensure_one()
+
+ # TODO: put action in the queue if jet is busy
+
+ # Action properties must be accessible despite of the user group
+ action = action.sudo()
+
+ # Get current command log
+ current_command_log = kwargs.get("current_command_log")
+
+ # Ensure the action is available for this jet
+ if action.id not in self.action_available_ids.ids:
+ error = _(
+ "Action '%(action)s' is not available for jet"
+ " '%(jet)s' in state '%(state)s'",
+ action=action.name,
+ jet=self.name, # pylint: disable=no-member
+ state=self.state_id.name if self.state_id else _("Undefined"),
+ )
+ if raise_if_not_available:
+ raise ValidationError(error)
+ if current_command_log:
+ current_command_log.finish(
+ status=JET_ACTION_NOT_AVAILABLE,
+ error=error,
+ )
+ return {"status": JET_ACTION_NOT_AVAILABLE, "error": error}
+
+ # Update the jet state
+ transit_state = action.state_transit_id
+ target_state = action.state_to_id
+
+ # Check if the jet is already in the target state
+ # TODO: handle the case when destination state
+ # is the same as the current state.
+ # Eg when a jet is restarted.
+ if self.state_id == target_state and from_transition:
+ self.sudo().write({"target_state_id": None})
+ self._finalize_transition(failed=False)
+ return {"status": 0, "error": None}
+
+ # Set target state if not already set
+ if not self.target_state_id:
+ self.sudo().write({"target_state_id": target_state})
+
+ # Check if all dependencies are satisfied
+ # if starting from an undefined state.
+ # This is typical for a newly created jet.
+ if not self.state_id and not self._control_dependencies():
+ # The process will be resumed
+ # when the dependencies are satisfied
+ error = _("Jet dependencies are not satisfied")
+ if current_command_log:
+ current_command_log.finish(
+ status=JET_DEPENDENCIES_NOT_SATISFIED,
+ error=error,
+ )
+ return {"status": JET_DEPENDENCIES_NOT_SATISFIED, "error": error}
+
+ self.sudo().write(
+ {
+ "state_id": transit_state,
+ "current_action_id": action.id,
+ "current_command_log_id": current_command_log.id
+ if current_command_log
+ else False,
+ }
+ )
+ if action.plan_id:
+ # Run the flight plan
+ plan_kwargs = {
+ "plan_log": {
+ "jet_action_id": action.id,
+ },
+ }
+ # Populate custom variable values from current command log
+ current_command_log = self.current_command_log_id
+ if current_command_log and current_command_log.variable_values:
+ plan_kwargs["variable_values"] = current_command_log.variable_values
+
+ # Run the flight plan
+ with self.env.cr.savepoint():
+ self.server_id.sudo().run_flight_plan(
+ flight_plan=action.plan_id,
+ jet=self,
+ **plan_kwargs,
+ )
+ # Flight plan will trigger the `_flight_plan_finished` function again
+ # if the flight plan is finished successfully.
+ # So we don't need continue the loop in this case.
+ return {"status": 0, "error": None}
+
+ # Set the state to the destination state if no plan is defined
+ final_vals = {
+ "state_id": target_state,
+ "current_action_id": False,
+ }
+
+ # Reset the target state if the jet has reached the target state
+ if target_state == self.target_state_id:
+ final_vals["target_state_id"] = None
+
+ self.sudo().write(final_vals)
+
+ # Continue the chain of actions if the final state is not reached yet
+ if self.target_state_id:
+ self._bring_to_state(self.target_state_id)
+
+ # Trigger the transition finished event
+ self._finalize_transition(failed=False)
+ return {"status": 0, "error": None}
+
+ def _bring_to_state(self, state=None):
+ """
+ Bring the jet to a specific state.
+
+ The function flow is:
+
+ 1. Compute the path of actions to bring the jet
+ to the target state.
+ 2. Set the target state.
+ 3. Trigger the first action in the path.
+ This will trigger a chain of actions until the jet is brought
+ into the target state.
+
+ IMPORTANT: this method uses sudo() to bypass access rules.
+ This means that this method must be used with caution and only in cases
+ where the access level is not important.
+ For external automation including Python commands always prefer using
+ the bring_to_state() method instead.
+ For example:
+ ```python
+ jet = self.env["cx.tower.jet"].browse(jet_id)
+ jet.bring_to_state(state_reference)
+ ```
+
+ Args:
+ state (cx.tower.jet.state()): The state to bring the jet to
+
+ Returns:
+ The jet is brought into the first state of the path.
+ In case of an error, the jet is brought into the error state
+ if the latter is defined.
+
+ Raises:
+ ValidationError: If the path is not found.
+ """
+ self.ensure_one()
+
+ # Use sudo to bypass access rules
+ self = self.sudo()
+
+ # Exit if jet is already in the target state
+ if self.state_id == state:
+ return
+
+ # Compute the path of actions to bring the jet to the target state
+ path = self.jet_template_id._get_action_path(
+ state_from=self.state_id, state_to=state
+ )
+ if not path:
+ raise ValidationError(
+ _(
+ "No path found to bring the jet %(jet)s to the state '%(state)s'",
+ jet=self.name, # pylint: disable=no-member
+ state=state.name if state else _("Undefined"),
+ )
+ )
+
+ # Set the target state if not already set
+ if not self.target_state_id:
+ self.write(
+ {
+ "target_state_id": state,
+ }
+ )
+
+ # Trigger the first action in the path
+ self._trigger_action(path[0], from_transition=True)
+
+ def _flight_plan_finished(self, plan_status):
+ """
+ Handle the completion of a flight plan.
+
+ Args:
+ plan_status (int): The status of the flight plan
+ (0: success, other: failure)
+ """
+ self.ensure_one()
+
+ # Used in case this is the last action in the chain
+ transition_failed = False
+
+ # Reset the current action
+ vals = {"current_action_id": False}
+
+ # If the flight plan is finished successfully,
+ # we bring the jet to the destination state
+ # of the current action
+ if plan_status == 0:
+ # Set the state to the destination state
+ vals["state_id"] = (
+ self.current_action_id.state_to_id
+ and self.current_action_id.state_to_id.id
+ )
+
+ # Reset the target state if the jet has reached the target state
+ # This will stop the chain of actions
+ if self.target_state_id == self.current_action_id.state_to_id:
+ vals["target_state_id"] = None
+
+ # If the flight plan is finished with an error,
+ # we bring the jet to the error state if it is defined
+ # or back to the initial state if not
+ # Reset the target state because we cannot continue the chain of actions
+ else:
+ vals.update(
+ {
+ "state_id": (
+ self.current_action_id.state_error_id
+ and self.current_action_id.state_error_id.id
+ )
+ or (
+ self.current_action_id.state_from_id
+ and self.current_action_id.state_from_id.id
+ ),
+ "target_state_id": None,
+ }
+ )
+ transition_failed = True
+
+ self.sudo().write(vals)
+
+ # Continue the chain of actions if the final state is not reached yet
+ if self.target_state_id:
+ self._bring_to_state(self.target_state_id)
+ else:
+ # Trigger the transition finished event
+ self._finalize_transition(failed=transition_failed)
+
+ def _finalize_transition(self, failed=False):
+ """
+ Handle the completion of a state transition.
+
+ Args:
+ failed (bool): True if the transition failed, False otherwise
+ """
+ self.ensure_one()
+
+ # 1. Finalize the jet request if it exists
+ if self.served_jet_request_id:
+ self.served_jet_request_id._finalize(failed=failed)
+
+ # 2. Finalize the command log if transition was
+ # triggered from a command
+ command_log = self.current_command_log_id
+ if command_log:
+ # Reset the current command log id
+ # Using sudo to bypass write access rules
+ self.sudo().write({"current_command_log_id": False})
+
+ # Prepare the command log finish values
+ if failed:
+ error = _(
+ "Action failed for jet %(jet)s.",
+ jet=self.name, # pylint: disable=no-member
+ )
+ response = None
+ status = JET_STATE_ERROR
+ else:
+ response = _(
+ "Jet %(jet)s was moved to the '%(state)s' state.",
+ jet=self.name, # pylint: disable=no-member
+ state=self.state_id.name if self.state_id else _("Undefined"),
+ )
+ status = 0
+ error = None
+
+ # Finish the command log
+ command_log.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+
+ # 3. Notify the jet that it is available
+ self._on_is_available()
+
+ def _serve_jet_request(self, jet_request):
+ """
+ Serve a jet request.
+
+ Args:
+ jet_request (cx.tower.jet.request()): The jet request to serve
+ """
+ self.ensure_one()
+
+ # Save the request
+ # Using sudo to bypass write access rules
+ self.sudo().write({"served_jet_request_id": jet_request.id})
+
+ # State is reached, finalize the request
+ if self.state_id == jet_request.state_requested_id:
+ jet_request._finalize(failed=False)
+ else:
+ # Trigger the jet to bring itself to the required state
+ jet_request.state = "processing"
+ self._bring_to_state(jet_request.state_requested_id)
+
+ def _finalize_jet_request(self, jet_request):
+ """
+ This function is called when a jet request issued by this jet is finalized.
+
+ Args:
+ jet_request (cx.tower.jet.request()): The jet request that was finalized
+ """
+ self.ensure_one()
+
+ # On success, update the dependency and
+ if jet_request.state == "success":
+ # Update the dependency if the request was for a dependency
+ dependency = jet_request.for_dependency_id
+ if dependency:
+ dependency.jet_depends_on_id = jet_request.jet_id
+ # Proceed with the state transition if all dependencies are satisfied
+ # and the transition is still in progress
+ if self._control_dependencies() and self.target_state_id:
+ self._bring_to_state(self.target_state_id)
+ else:
+ # Stop transition if the request failed
+ # Using sudo to bypass write access rules
+ self.sudo().write({"target_state_id": False})
+ # Mark served jet request as failed
+ if self.served_jet_request_id:
+ self.served_jet_request_id._finalize(failed=True)
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Waypoints
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def create_waypoint(
+ self,
+ waypoint_template,
+ name=None,
+ fly_here=False,
+ ignore_busy=False,
+ created_from_command_log=None,
+ **metadata,
+ ):
+ """Create a new waypoint for the jet.
+
+ The jet must not be busy unless ignore_busy is True.
+ When created_from_command_log is provided, the waypoint stores it so that
+ the waypoint callback can finish the command log when the waypoint
+ reaches ready/current or error.
+
+ Args:
+ waypoint_template (cx.tower.jet.waypoint.template or str):
+ The waypoint template or reference to create the waypoint from.
+ name (str, optional): The name of the waypoint. Defaults to None.
+ fly_here (bool, optional): Whether to fly to the waypoint after creation.
+ Defaults to False.
+ ignore_busy (bool, optional): Whether to ignore the busy state and create
+ the waypoint anyway.
+ Useful when creating waypoints from jet actions.
+ Defaults to False.
+ created_from_command_log (cx.tower.command.log, optional): Command log
+ that created this waypoint; the waypoint callback will finish it.
+ Defaults to None.
+ **metadata: Additional metadata to pass to the waypoint.
+
+ Returns:
+ cx.tower.jet.waypoint
+
+ Raises:
+ ValidationError: If the waypoint template is not found
+ or does not belong to the jet template, or if the jet is busy.
+ """
+ self.ensure_one()
+
+ # Check if the jet is busy
+ if self._is_busy() and not ignore_busy:
+ _logger.error(
+ "Cannot create waypoint for jet %s because it is busy", self.name
+ )
+ raise ValidationError(
+ _("Cannot create waypoint for jet %s because it is busy", self.name)
+ )
+
+ # Resolve the waypoint template
+ if isinstance(waypoint_template, str):
+ waypoint_reference = waypoint_template
+ waypoint_template = self.env[
+ "cx.tower.jet.waypoint.template"
+ ].get_by_reference(waypoint_reference)
+ if not waypoint_template:
+ _logger.error("Waypoint template %s not found", waypoint_reference)
+ raise ValidationError(
+ _("Waypoint template %s not found", waypoint_reference)
+ )
+
+ # Check if the waypoint template belongs to the jet template
+ if waypoint_template.jet_template_id != self.jet_template_id:
+ _logger.error(
+ "Waypoint template %s does not belong to the jet template %s",
+ waypoint_template.name,
+ self.jet_template_id.name,
+ )
+ raise ValidationError(
+ _(
+ "Waypoint template %(waypoint_template)s does not belong "
+ "to the jet template %(jet_template)s",
+ waypoint_template=waypoint_template.name,
+ jet_template=self.jet_template_id.name,
+ )
+ )
+
+ # Prepare the waypoint values
+ waypoint_values = self._prepare_waypoint_values(
+ waypoint_template=waypoint_template,
+ name=name,
+ **metadata,
+ )
+ if created_from_command_log:
+ waypoint_values["created_from_command_log_id"] = created_from_command_log.id
+
+ # Create the waypoint
+ waypoint = self.env["cx.tower.jet.waypoint"].create(waypoint_values)
+ waypoint.prepare(is_destination=fly_here)
+ return waypoint
+
+ def _prepare_waypoint_values(self, waypoint_template, name=None, **metadata):
+ """Prepare the waypoint values
+
+ Args:
+ waypoint_template (cx.tower.jet.waypoint.template): The waypoint template
+ name (Char, optional): The name of the waypoint.
+ """
+ self.ensure_one()
+
+ # Prepare the waypoint values
+ vals = {
+ "waypoint_template_id": waypoint_template.id,
+ "name": name if name else _("Auto-generated waypoint"),
+ "jet_id": self.id,
+ }
+ if metadata:
+ vals["metadata"] = metadata
+
+ return vals
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Event handling
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def _on_state_exit(self, state=None):
+ """
+ Handle the exit of the jet from a state.
+
+ Args:
+ state (cx.tower.jet.state()): The state jet is exiting
+ """
+ self.ensure_one()
+ # TODO: Implement the logic to handle the exit of the jet from a state
+ pass
+
+ def _on_state_enter(self, state=None):
+ """
+ Handle the entry of the jet into a state.
+
+ Args:
+ state (cx.tower.jet.state()): The state jet is entering
+ """
+ self.ensure_one()
+
+ # Refresh the frontend views
+ self.env.user.reload_views(model="cx.tower.jet", rec_ids=[self.id])
+
+ def _on_jet_request_completed(self, jet_request):
+ """
+ Handle the completion of a jet request.
+ """
+ self.ensure_one()
+ # TODO: Implement the logic to handle the completion of a jet request
+ pass
+
+ def _on_is_available(self):
+ """
+ Handle the event when the jet is not busy anymore.
+ """
+
+ # Process pending requests
+ jet_request_obj = self.env["cx.tower.jet.request"].sudo()
+
+ # 1. Requests where the jet is requested explicitly
+ explicit_requests = jet_request_obj.search(
+ [
+ ("jet_id", "=", self.id), # pylint: disable=no-member
+ ("state", "=", "new"),
+ ]
+ )
+ if explicit_requests:
+ # Check which state is required by the request
+ # TODO: IMPORTANT: we must find a workaround to avoid infinite loops
+ # when different jets keep requesting the same target jet in different
+ # states and the target jet keeps jumping from one state to another.
+
+ # Finalize all requests that request the same state as the jet
+ same_state_requests = explicit_requests.filtered(
+ lambda r: r.state_requested_id == self.state_id
+ )
+ for request in same_state_requests:
+ request._finalize(failed=False)
+
+ # Pick the first request that requests a different state
+ remaining_requests = explicit_requests - same_state_requests
+ if remaining_requests:
+ self._serve_jet_request(remaining_requests[0])
+ return
+
+ # 2. Requests where the jet is requested implicitly via template
+ if self._accepts_new_links():
+ implicit_requests = jet_request_obj.search(
+ [
+ ("server_id", "=", self.server_id.id), # pylint: disable=no-member
+ ("jet_template_id", "=", self.jet_template_id.id), # pylint: disable=no-member
+ ("jet_id", "=", False),
+ ("state", "=", "new"),
+ ]
+ )
+ same_state_requests = implicit_requests.filtered(
+ lambda r: r.state_requested_id == self.state_id
+ )
+ if same_state_requests:
+ # Set current jet as the target jet for the requests
+ same_state_requests.write({"jet_id": self.id}) # pylint: disable=no-member
+ for request in same_state_requests:
+ request._finalize(failed=False)
+
+ # Pick the first request that requests a different state
+ remaining_requests = implicit_requests - same_state_requests
+ if remaining_requests:
+ remaining_request = remaining_requests[0]
+ # Set current jet as the target jet for the request
+ remaining_request.write({"jet_id": self.id}) # pylint: disable=no-member
+ self._serve_jet_request(remaining_request)
+ return
+
+ # Send success notification when everything is done
+ # Use context timestamp to avoid timezone issues
+ context_timestamp = fields.Datetime.context_timestamp(
+ self, fields.Datetime.now()
+ )
+
+ # 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"
+ )
+ if notification_type_success:
+ # Action for button
+ action = self.env["ir.actions.act_window"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_action"
+ )
+
+ context = self.env.context.copy()
+ params = dict(context.get("params") or {})
+ params["button_name"] = _("View Jet")
+ context["params"] = params
+
+ # Add record id and context to the action
+ action.update(
+ {
+ "context": context,
+ "res_id": self.id,
+ "views": [(False, "form")],
+ }
+ )
+ # Send success notification
+ self.env.user.notify_success(
+ message=_(
+ "%(timestamp)s " "Available in the '%(name)s' state",
+ name=self.state_id.name if self.state_id else _("Undefined"),
+ timestamp=context_timestamp,
+ ),
+ title=self.name,
+ sticky=notification_type_success == "sticky",
+ action=action,
+ )
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Status and busyness
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def _accepts_new_links(self):
+ """
+ Check if the jet is available to accept new links from other jets.
+
+ Returns:
+ bool: True if the jet is available to accept new links from other jets,
+ False otherwise
+ """
+ self.ensure_one()
+ # TODO: Implement the logic to check if the jet is available
+ # to accept new links from other jets
+ return True
+
+ def _is_busy(self):
+ """
+ Check if the jet is busy with some other action.
+ Overwrite this function to implement custom logic.
+
+ Returns:
+ bool: True if the jet is busy with some other action,
+ False otherwise
+ """
+ self.ensure_one()
+
+ # Jet is considered busy if it is currently transitioning to another state
+ busy = bool(self.target_state_id)
+ return busy
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Manage dependencies
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ def _control_dependencies(self):
+ """
+ Check if dependencies are satisfied.
+ If some dependencies are missing, it creates a new jet request to ensure
+ that a jet that is required by that dependency is available.
+
+ Returns:
+ bool: True if all dependencies are satisfied, False otherwise
+ """
+ self.ensure_one()
+
+ all_dependencies_satisfied = True
+
+ jet_request_obj = self.env["cx.tower.jet.request"]
+
+ # Check if jets are present in the required state
+ for jet_dependency in self.jet_requires_ids:
+ jet_template_dependency = jet_dependency.jet_template_dependency_id
+ if (
+ jet_dependency.jet_depends_on_id
+ and jet_dependency.jet_depends_on_id.state_id
+ == jet_template_dependency.state_required_id
+ ):
+ # The dependency is satisfied, continue to the next dependency
+ continue
+
+ # Create a new jet request to ensure we have the required jet
+ # in the required state
+ jet_request_obj._create_request(
+ server=self.server_id,
+ jet_template=jet_template_dependency.template_required_id,
+ state=jet_template_dependency.state_required_id,
+ requested_by_jet=self,
+ for_dependency=jet_dependency,
+ )
+ # Stop here as it will be resumed when the jet request is finalized
+ all_dependencies_satisfied = False
+ break
+
+ return all_dependencies_satisfied
+
+ def _get_dependent_jets_by_template(self, jet_template):
+ """
+ Check all dependencies of the jet and returns all jets
+ of the given template.
+ Both dependent and this jet depends on jets are returned.
+
+ Args:
+ jet_template (cx.tower.jet.template()): The jet template
+
+ Returns:
+ cx.tower.jet(): Recordset of jets
+ """
+ self.ensure_one()
+
+ # Check L1 jets this jet depends on
+ l1_jets = self.jet_requires_ids.filtered(
+ lambda r: r.jet_depends_on_id.jet_template_id == jet_template
+ ).jet_depends_on_id
+ # Check L1 jets that depend on this jet
+ l2_jets = self.jet_required_by_ids.filtered(
+ lambda r: r.jet_id.jet_template_id == jet_template
+ ).jet_id
+
+ # TODO: check the entire dependency tree
+ return l1_jets | l2_jets
+
+ def get_dependent_jets_by_template_reference(self, jet_template_reference):
+ """
+ A wrapper for _get_dependent_jets_by_template that allows
+ to use the reference of the jet template instead of the record.
+ Designed to be used in the Python commands.
+
+ Args:
+ jet_template_reference (str): The reference of the jet template
+
+ Returns:
+ cx.tower.jet(): Recordset of jets with the given template
+ that depend on the current jet.
+ """
+ self.ensure_one()
+
+ jet_template = self.jet_template_id.get_by_reference(jet_template_reference)
+ if jet_template:
+ return self._get_dependent_jets_by_template(jet_template)
+ return False
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Access role mixin functions
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def _get_post_create_fields(self):
+ """
+ Add fields that should be populated after jet template creation
+ """
+ res = super()._get_post_create_fields()
+ return res + ["variable_value_ids", "server_log_ids"]
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_action.py b/addons/cetmix_tower_server/models/cx_tower_jet_action.py
new file mode 100644
index 0000000..6f4d991
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_action.py
@@ -0,0 +1,100 @@
+# Copyright (C) 2024 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 CxTowerJetAction(models.Model):
+ """Jet Actions represent transitions between states in a jet's lifecycle"""
+
+ _name = "cx.tower.jet.action"
+ _description = "Cetmix Tower Jet Action"
+ _inherit = ["cx.tower.reference.mixin", "cx.tower.access.mixin"]
+ _order = "priority, id"
+
+ active = fields.Boolean(related="jet_template_id.active", readonly=True)
+ priority = fields.Integer(default=10, required=True)
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ string="Jet Template",
+ help="Jet template that this action belongs to",
+ ondelete="cascade",
+ )
+ color = fields.Integer(related="state_to_id.color", readonly=True)
+ note = fields.Text()
+
+ # -- State Transitions
+ state_from_id = fields.Many2one(
+ comodel_name="cx.tower.jet.state",
+ string="From State",
+ help="Source state for this transition. Leave blank for an initial state",
+ ondelete="restrict",
+ )
+
+ state_transit_id = fields.Many2one(
+ comodel_name="cx.tower.jet.state",
+ string="Transit State",
+ required=True,
+ help="Intermediate state during the transition",
+ ondelete="restrict",
+ )
+
+ state_to_id = fields.Many2one(
+ comodel_name="cx.tower.jet.state",
+ string="To State",
+ help="Destination state for this transition. Leave blank for a final state",
+ ondelete="restrict",
+ )
+
+ state_error_id = fields.Many2one(
+ comodel_name="cx.tower.jet.state",
+ string="Error State",
+ help="State to transition to if an error occurs",
+ ondelete="restrict",
+ )
+
+ plan_id = fields.Many2one(
+ string="Flight Plan",
+ comodel_name="cx.tower.plan",
+ help="Flight plan to execute when this action is triggered",
+ )
+
+ # TODO: ensure that all actions belong to the same jet template
+
+ def trigger(self, jet=None):
+ """Trigger jet action on a given jet.
+ If jet is not provided, the action will be triggered on the jet
+ in the context key "jet_id".
+
+ Args:
+ jet (cx.tower.jet): Jet to trigger the action.
+ """
+ self.ensure_one()
+
+ # Try to obtain jet from context if not provided as an argument
+ if jet is None:
+ jet_id = self.env.context.get("jet_id")
+
+ # Just return, no exceptions for now
+ if not jet_id:
+ return
+
+ jet = self.env["cx.tower.jet"].browse(jet_id)
+
+ # Ensure that the action is for a single jet
+ if not jet or len(jet) > 1:
+ raise ValidationError(_("Action can be triggered only for a single jet"))
+
+ # Trigger the action
+ jet._trigger_action(self)
+
+ # ------------------------------
+ # Reference mixin methods
+ # ------------------------------
+ def _get_pre_populated_model_data(self):
+ res = super()._get_pre_populated_model_data()
+ res.update(
+ {"cx.tower.jet.action": ["cx.tower.jet.template", "jet_template_id"]}
+ )
+ return res
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_dependency.py b/addons/cetmix_tower_server/models/cx_tower_jet_dependency.py
new file mode 100644
index 0000000..4775135
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_dependency.py
@@ -0,0 +1,63 @@
+# 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 CxTowerJetDependency(models.Model):
+ """Model to manage dependent Jets"""
+
+ _name = "cx.tower.jet.dependency"
+ _description = "Cetmix Tower Jet Dependency"
+ _log_access = False
+
+ jet_template_dependency_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template.dependency",
+ string="Jet Template Dependency",
+ index=True,
+ help="Related jet template dependency. "
+ "Used to track dependency changes at the template level.",
+ ondelete="cascade",
+ )
+ jet_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ string="Jet",
+ required=True,
+ index=True,
+ help="Jet this dependency belongs to",
+ ondelete="cascade",
+ )
+ jet_depends_on_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ string="Depends On",
+ index=True,
+ help="Jet this Jet depends on.",
+ ondelete="cascade",
+ )
+
+ _sql_constraints = [
+ (
+ "unique_jet_dependency",
+ "UNIQUE(jet_id, jet_depends_on_id)",
+ "This dependency already exists!",
+ )
+ ]
+
+ @api.constrains("jet_id", "jet_depends_on_id", "jet_template_dependency_id")
+ def _check_self_dependency(self):
+ for record in self:
+ # Ensure jet dependency is not a self-dependency
+ if record.jet_id == record.jet_depends_on_id:
+ raise ValidationError(_("A jet cannot depend on itself!"))
+ # Ensure jet that we depend on has the template
+ # from the template dependency
+ if (
+ record.jet_depends_on_id
+ and record.jet_template_dependency_id
+ and record.jet_depends_on_id.jet_template_id
+ != record.jet_template_dependency_id.template_required_id
+ ):
+ raise ValidationError(
+ _("A jet cannot depend on a jet with a different template!")
+ )
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_request.py b/addons/cetmix_tower_server/models/cx_tower_jet_request.py
new file mode 100644
index 0000000..6228097
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_request.py
@@ -0,0 +1,260 @@
+# Copyright (C) 2024 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
+
+_logger = logging.getLogger(__name__)
+
+
+class CxTowerJetRequest(models.Model):
+ """
+ Requests for jets. Issued when there is a jet needed in a specific
+ state on a server.
+
+ Eg. jet "Application" needs a jet "Database" to be in state "Running"
+ to be able to start.
+ It looks for an existing jet in the required state and if not found,
+ creates a jet request.
+
+ During the request processing, Tower will try to find and existing jet and
+ bring it to the required state. Or create a new one if not found.
+
+ When a request is finalized, it will report the result to the request issuer
+ using the callback function.
+
+ """
+
+ _name = "cx.tower.jet.request"
+ _description = "Cetmix Tower Jet Request"
+
+ server_id = fields.Many2one(
+ comodel_name="cx.tower.server",
+ required=True,
+ ondelete="cascade",
+ copy=False,
+ help="Server where the jet is requested",
+ )
+ jet_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ ondelete="cascade",
+ string="Serviced by Jet",
+ copy=False,
+ help="Jet that is requested",
+ )
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ required=True,
+ string="Requested Template",
+ ondelete="cascade",
+ copy=False,
+ help="Template of the jet that is requested. "
+ "Used to create a new jet if not found.",
+ )
+ state_requested_id = fields.Many2one(
+ comodel_name="cx.tower.jet.state",
+ ondelete="cascade",
+ copy=False,
+ help="State of the jet that is requested",
+ )
+ requested_by_jet_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ ondelete="cascade",
+ string="Requested by Jet",
+ copy=False,
+ help="Jet that is requesting the jet",
+ )
+ for_dependency_id = fields.Many2one(
+ comodel_name="cx.tower.jet.dependency",
+ ondelete="cascade",
+ copy=False,
+ help="Dependency for which request is created",
+ )
+ state = fields.Selection(
+ selection=[
+ ("new", "New"),
+ ("processing", "Processing"),
+ ("success", "Success"),
+ ("failed", "Failed"),
+ ],
+ default="new",
+ required=True,
+ copy=False,
+ )
+
+ @api.model
+ def _create_request(
+ self,
+ server,
+ jet=None,
+ jet_template=None,
+ state=None,
+ requested_by_jet=None,
+ for_dependency=None,
+ ):
+ """
+ Create a new jet request.
+
+ Args:
+ server (cx.tower.server()): Server to create the request on
+ jet (cx.tower.jet()): Jet to create the request for
+ jet_template (cx.tower.jet.template()): Template to create the request for
+ state (cx.tower.jet.state()): State to create the request for
+ requested_by_jet (cx.tower.jet()): Jet that is requesting the jet
+ for_dependency (cx.tower.jet.dependency()): Dependency for which request
+ is created
+
+ Returns:
+ cx.tower.jet.request(): A jet request for the jet
+ """
+
+ # Must have either jet or jet template
+ if not jet and not jet_template:
+ raise ValidationError(
+ _("Either a jet or a jet template must be provided to create a request")
+ )
+
+ # Set jet template from the jet if not provided
+ if not jet_template and jet:
+ jet.ensure_one()
+ jet_template = jet.jet_template_id
+
+ request = self.env["cx.tower.jet.request"].create(
+ {
+ "server_id": server.id,
+ "jet_id": jet.id if jet else None,
+ "jet_template_id": jet_template.id if jet_template else None,
+ "state_requested_id": state.id if state else None,
+ "requested_by_jet_id": requested_by_jet.id
+ if requested_by_jet
+ else None,
+ "for_dependency_id": for_dependency.id if for_dependency else None,
+ }
+ )
+
+ # Step 1. Use the existing jet if provided explicitly
+ if jet:
+ if jet.server_id != server:
+ raise ValidationError(
+ _(
+ "Jet %(jet)s is not on server %(server)s",
+ jet=jet.name,
+ server=server.name,
+ )
+ )
+ if jet.state_id == state and not jet._is_busy():
+ _logger.info(
+ "Jet %s is available and not busy, finalizing request", jet.name
+ )
+ request._finalize(failed=False)
+ elif jet.target_state_id == state:
+ _logger.info(
+ "Jet %s is transitioning to the target state, "
+ "waiting for it to finish",
+ jet.name,
+ )
+ jet._serve_jet_request(jet_request=request)
+ else:
+ _logger.info(
+ "Jet %s is not available or busy, triggering jet to "
+ "bring itself to the required state",
+ jet.name,
+ )
+ jet._serve_jet_request(jet_request=request)
+ return request
+
+ # Step 2. Try to pick any of the existing jets from the template
+ available_jets = jet_template.jet_ids.filtered(
+ lambda j: j.server_id == server and j._accepts_new_links()
+ )
+ for available_jet in available_jets:
+ # Finalize the request instantly if the jet state
+ # matches and jet is not busy
+ if available_jet.state_id == state and not available_jet._is_busy():
+ _logger.info(
+ "Jet %s is available and not busy, finalizing request",
+ available_jet.name,
+ )
+ request.jet_id = available_jet
+ request._finalize(failed=False)
+ return request
+
+ # Step 3. Jet is available, and is not busy, but not in the required state
+ transitioning_jets = available_jets.filtered(
+ lambda j: j.target_state_id == state
+ )
+ if transitioning_jets:
+ _logger.info(
+ "Jet %s is transitioning to the target state, "
+ "waiting for it to finish",
+ transitioning_jets[0].name,
+ )
+ # Trigger the jet to bring itself to the required state
+ request.jet_id = transitioning_jets[0]
+ return request
+
+ # Step 4. Jet is available, and is not busy, but not in the required state
+ not_busy_jets = available_jets.filtered(lambda j: not j._is_busy())
+ if not_busy_jets:
+ # Pick the first available jet
+ not_busy_jet = not_busy_jets[0]
+ _logger.info(
+ "Jet %s is available and not busy, but not in the required state,"
+ " triggering jet to bring itself to the required state",
+ not_busy_jet.name,
+ )
+ # Trigger the jet to bring itself to the required state
+ request.jet_id = not_busy_jet
+ not_busy_jet._serve_jet_request(jet_request=request)
+ return request
+
+ # Step 5. Jet is not available, or is busy and not transitioning
+ # to the required state - create a new jet
+ # TODO: Add an option to wait for the jet to become available
+ if jet_template:
+ jet_template.ensure_one()
+ _logger.info("Creating new jet using template %s", jet_template.name)
+ jet = jet_template.create_jet(server)
+ if jet:
+ _logger.info("Created new jet %s", jet.name)
+ request.jet_id = jet
+ if jet.state_id == state:
+ request._finalize(failed=False)
+ else:
+ # Trigger the jet to bring itself to the required state
+ jet._serve_jet_request(jet_request=request)
+ else:
+ _logger.error(
+ "Failed to create new jet using template %s", jet_template.name
+ )
+ request._finalize(failed=True)
+
+ _logger.info("Jet request creation finished")
+ return request
+
+ def _finalize(self, failed=False):
+ """
+ Finalize a jet request.
+
+ Args:
+ failed (bool): Whether the request failed
+ """
+ self.ensure_one()
+
+ # 1. Update the state of the request
+ self.write(
+ {
+ "state": "success" if not failed else "failed",
+ }
+ )
+
+ # 2. Notify the jet that issued the request
+ if self.requested_by_jet_id:
+ self.requested_by_jet_id._finalize_jet_request(self)
+
+ # 3. Remove the link to the jet that was handling the request
+ if self.jet_id and self.jet_id.served_jet_request_id == self:
+ # Unlink the jet from the request
+ self.jet_id.sudo().write({"served_jet_request_id": False})
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_state.py b/addons/cetmix_tower_server/models/cx_tower_jet_state.py
new file mode 100644
index 0000000..5eff637
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_state.py
@@ -0,0 +1,90 @@
+# Copyright (C) 2024 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import _, fields, models
+from odoo.exceptions import AccessError, ValidationError
+
+
+class CxTowerJetState(models.Model):
+ """Jet States represent the different states a jet can be in during its lifecycle"""
+
+ _name = "cx.tower.jet.state"
+ _description = "Cetmix Tower Jet State"
+ _inherit = ["cx.tower.reference.mixin", "cx.tower.access.mixin"]
+ _order = "sequence, id"
+
+ sequence = fields.Integer(default=10, required=True)
+ active = fields.Boolean(default=True)
+ color = fields.Integer()
+ note = fields.Text()
+
+ # Set default access level to User
+ access_level = fields.Selection(default="1")
+
+ def unlink(self):
+ """
+ Do not allow to unlink a state
+ if it is used in any action
+ """
+ actions = self.env["cx.tower.jet.action"].search(
+ [
+ "|",
+ "|",
+ ("state_from_id", "in", self.ids),
+ ("state_to_id", "in", self.ids),
+ ("state_transit_id", "in", self.ids),
+ ]
+ )
+ if actions:
+ raise ValidationError(
+ _(
+ "Some states are still used in the following actions: %(actions)s"
+ "\nJet templates: %(templates)s",
+ actions=", ".join(set(actions.mapped("name"))),
+ templates=", ".join(set(actions.mapped("jet_template_id.name"))),
+ )
+ )
+ return super().unlink()
+
+ def set_state(self, jet=None):
+ """Sets the state of the jet
+
+ Args:
+ jet (cx.tower.jet): Jet to set the state.
+ """
+ self.ensure_one()
+
+ # Try to obtain jet from context if not provided as an argument
+ if jet is None:
+ jet_id = self.env.context.get("jet_id")
+
+ # Just return, no exceptions for now
+ if not jet_id:
+ return
+
+ jet = self.env["cx.tower.jet"].browse(jet_id)
+
+ # Ensure that the state is set for a single jet
+ if not jet or len(jet) > 1:
+ raise ValidationError(_("State can be set only for a single jet"))
+
+ # Check access to the jet
+ jet.check_access("read")
+
+ # Get user access level
+ user_access_level = self.env.user._cetmix_tower_access_level()
+
+ # If user is manager but is not added as a manager to the jet,
+ # his access level is considered as user.
+ # NB: record access is already checked above.
+ if user_access_level == "2" and self.env.user not in jet.manager_ids:
+ user_access_level = "1"
+
+ # Check if user access level is equal or greater
+ if self.access_level > user_access_level:
+ raise AccessError(
+ _("You are not allowed to set the '%(state)s' state!", state=self.name)
+ )
+
+ # Bring the jet to the state
+ jet._bring_to_state(self)
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_template.py b/addons/cetmix_tower_server/models/cx_tower_jet_template.py
new file mode 100644
index 0000000..3f740c3
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_template.py
@@ -0,0 +1,1446 @@
+# Copyright (C) 2024 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+import ast
+import base64
+import heapq
+import logging
+import xml.etree.ElementTree as ET
+from collections import defaultdict
+
+from odoo import _, api, fields, models
+from odoo.exceptions import ValidationError
+
+from .tools import generate_random_id, is_valid_url
+
+_logger = logging.getLogger(__name__)
+
+
+# Maximum number of retries to generate a unique jet name
+# Used to prevent infinite loop
+MAX_JET_NAME_RETRIES = 50
+
+
+class CxTowerJetTemplate(models.Model):
+ """Jet Templates are templates to create and manage jets"""
+
+ _name = "cx.tower.jet.template"
+ _description = "Cetmix Tower Jet Template"
+ _inherit = [
+ "cx.tower.reference.mixin",
+ "cx.tower.access.mixin",
+ "cx.tower.access.role.mixin",
+ "cx.tower.variable.mixin",
+ "mail.thread",
+ "cx.tower.tag.mixin",
+ ]
+ _order = "name asc"
+ _mail_post_access = "read"
+
+ active = fields.Boolean(default=True)
+ icon = fields.Image(
+ string="Icon image",
+ max_width=128,
+ max_height=128,
+ help="Icon of the related product to make navigation easier. "
+ "E.g. Docker logo for the Docker jet template.",
+ )
+ note = fields.Text()
+
+ # ---- Access. Add relation for mixin fields
+ user_ids = fields.Many2many(
+ relation="cx_tower_jet_template_user_rel",
+ )
+ manager_ids = fields.Many2many(
+ relation="cx_tower_jet_template_manager_rel",
+ )
+
+ # Jets
+ jet_ids = fields.One2many(
+ comodel_name="cx.tower.jet",
+ inverse_name="jet_template_id",
+ string="Jets",
+ copy=False,
+ )
+ jet_count = fields.Integer(compute="_compute_jet_count", store=False)
+
+ # Servers
+ server_ids = fields.Many2many(
+ comodel_name="cx.tower.server",
+ relation="cx_tower_jet_template_server_rel",
+ column1="jet_template_id",
+ column2="server_id",
+ string="Installed on Servers",
+ readonly=True,
+ help="These servers have this jet template installed",
+ copy=False,
+ )
+ limit_per_server = fields.Integer(
+ string="Limit per Server",
+ help="Maximum number of Jets that can be launched on a server. "
+ "Set to 0 for no limit.",
+ )
+ file_ids = fields.One2many(
+ comodel_name="cx.tower.file",
+ inverse_name="jet_template_id",
+ string="Files",
+ help="Files of this jet template",
+ copy=False,
+ )
+
+ # Wizards
+ show_in_create_wizard = fields.Boolean(
+ string="Show in Wizard",
+ help="If enabled, the template will be shown "
+ "in the wizard to create a new jet",
+ )
+
+ # Flight Plans
+ plan_install_id = fields.Many2one(
+ comodel_name="cx.tower.plan",
+ string="Installation Flight Plan",
+ help="Flight plan used to install the template from a server",
+ )
+ plan_uninstall_id = fields.Many2one(
+ comodel_name="cx.tower.plan",
+ string="Uninstallation Flight Plan",
+ help="Flight plan used to uninstall the template from a server",
+ )
+ plan_clone_same_server_id = fields.Many2one(
+ comodel_name="cx.tower.plan",
+ help="Flight plan used to clone the jet on the same server",
+ )
+ plan_clone_different_server_id = fields.Many2one(
+ comodel_name="cx.tower.plan",
+ help="Flight plan used to clone the jet to a different server",
+ )
+
+ # Logs
+ command_log_ids = fields.One2many(
+ comodel_name="cx.tower.command.log",
+ inverse_name="jet_template_id",
+ copy=False,
+ )
+ plan_log_ids = fields.One2many(
+ comodel_name="cx.tower.plan.log",
+ inverse_name="jet_template_id",
+ copy=False,
+ )
+
+ # Server logs
+ server_log_ids = fields.One2many(
+ comodel_name="cx.tower.server.log",
+ inverse_name="jet_template_id",
+ copy=True,
+ )
+ # Scheduled Tasks
+ scheduled_task_ids = fields.Many2many(
+ comodel_name="cx.tower.scheduled.task",
+ relation="cx_tower_jet_template_scheduled_task_rel",
+ column1="jet_template_id",
+ column2="scheduled_task_id",
+ string="Scheduled Tasks",
+ copy=True,
+ )
+
+ # Configuration variables
+ variable_value_ids = fields.One2many(
+ inverse_name="jet_template_id",
+ copy=True,
+ )
+
+ # Actions
+ action_ids = fields.One2many(
+ comodel_name="cx.tower.jet.action",
+ inverse_name="jet_template_id",
+ string="Lifecycle Actions",
+ copy=True,
+ )
+ action_create_id = fields.Many2one(
+ comodel_name="cx.tower.jet.action",
+ string="Create Jet",
+ help="The action is used to create a new Jet",
+ compute="_compute_border_actions",
+ readonly=False,
+ store=True,
+ domain="[('state_from_id', '=', False), "
+ "('state_to_id', '!=', False),"
+ " ('jet_template_id', '=', id)]",
+ copy=False,
+ )
+ action_destroy_id = fields.Many2one(
+ comodel_name="cx.tower.jet.action",
+ string="Destroy Jet",
+ compute="_compute_border_actions",
+ readonly=False,
+ store=True,
+ help="The action is used to destroy a Jet",
+ domain="[('state_to_id', '=', False), ('jet_template_id', '=', id)]",
+ copy=False,
+ )
+
+ # Dependencies
+ template_requires_ids = fields.One2many(
+ comodel_name="cx.tower.jet.template.dependency",
+ inverse_name="template_id",
+ string="Requires",
+ help="Define other templates that must be in specific"
+ " states for this template to function",
+ copy=True,
+ groups="cetmix_tower_server.group_manager",
+ )
+ template_required_by_ids = fields.One2many(
+ comodel_name="cx.tower.jet.template.dependency",
+ inverse_name="template_required_id",
+ string="Required by",
+ help="Define other templates that require this template"
+ " to be in a specific"
+ " state to function",
+ groups="cetmix_tower_server.group_manager",
+ )
+
+ # Installation
+ install_ids = fields.One2many(
+ comodel_name="cx.tower.jet.template.install.line",
+ inverse_name="jet_template_id",
+ string="Installations",
+ help="Installations of the template",
+ auto_join=True,
+ copy=False,
+ groups="cetmix_tower_server.group_manager",
+ readonly=True,
+ )
+
+ # Waypoints
+ waypoint_template_ids = fields.One2many(
+ comodel_name="cx.tower.jet.waypoint.template",
+ inverse_name="jet_template_id",
+ string="Waypoints",
+ help="Waypoints of the template",
+ copy=True,
+ )
+
+ # Dependency Graph
+ # Odoo blocks SVG images in fields.Binary,
+ # so we use fields.Char to store the SVG content
+ # https://github.com/odoo/odoo/blob/c27d978ade9bcbea056933d8fb8b5a924e983bde/odoo/fields.py#L2321
+ dependency_graph_svg = fields.Char(
+ compute="_compute_dependency_graph_svg",
+ store=True,
+ recursive=True,
+ copy=False,
+ help="SVG image content of the dependency graph of the template",
+ )
+ dependency_graph_image = fields.Binary(
+ string="Dependency Graph",
+ compute="_compute_dependency_graph_image",
+ compute_sudo=True,
+ help="SVG image of the dependency graph of the template",
+ )
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Compute functions
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ @api.depends("jet_ids")
+ def _compute_jet_count(self):
+ """Compute the number of jets for each template."""
+ for template in self:
+ template.jet_count = len(template.jet_ids)
+
+ @api.depends(
+ "action_ids",
+ "action_ids.state_from_id",
+ "action_ids.state_to_id",
+ "action_ids.priority",
+ )
+ def _compute_border_actions(self):
+ """Compute the 'Create Jet' and 'Destroy Jet' actions"""
+ for template in self:
+ # If no initial state, add the one automatically
+ if not template.action_create_id:
+ # Has no initial state and has a final state
+ suitable_actions = template.action_ids.filtered(
+ lambda a: not a.state_from_id and a.state_to_id
+ ).sorted("priority")
+ # Take the first one (lowest priority = highest priority)
+ if suitable_actions:
+ template.action_create_id = suitable_actions[0]
+
+ # If "Create" action has an initial state
+ # or does not have a final state
+ # it cannot be used to create a new Jet
+ elif (
+ template.action_create_id.state_from_id
+ or not template.action_create_id.state_to_id
+ ):
+ template.action_create_id = False
+
+ if not template.action_destroy_id:
+ # Has no final state
+ suitable_actions = template.action_ids.filtered(
+ lambda a: not a.state_to_id
+ ).sorted("priority")
+ # Take the first one (lowest priority = highest priority)
+ if suitable_actions:
+ template.action_destroy_id = suitable_actions[0]
+
+ # If "Destroy" action has a final state
+ # it cannot be used to destroy a Jet
+ elif template.action_destroy_id.state_to_id:
+ template.action_destroy_id = False
+
+ @api.depends(
+ "template_requires_ids",
+ "template_requires_ids.state_required_id",
+ "template_requires_ids.template_required_id.dependency_graph_image",
+ )
+ def _compute_dependency_graph_svg(self):
+ """Compute dependency graph image using SVG generation"""
+ for template in self:
+ try:
+ graph_data = template._build_dependency_graph()
+ svg_content = template._generate_svg_graph(graph_data)
+ template.dependency_graph_svg = svg_content
+ except Exception as e:
+ _logger.error(
+ f"Error generating dependency graph "
+ f"for template {template.name}: {e}"
+ )
+ template.dependency_graph_svg = False
+
+ @api.depends("dependency_graph_svg")
+ def _compute_dependency_graph_image(self):
+ for template in self:
+ template.dependency_graph_image = template.dependency_graph_svg
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # ORM methods
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def unlink(self):
+ """
+ Unlink all related files
+ """
+
+ # Don't allow to unlink a template if it has any jets
+ # or is installed on any server
+ templates_with_jets = self.filtered(lambda t: t.jet_ids)
+ if templates_with_jets:
+ raise ValidationError(
+ _(
+ "Following templates cannot be deleted "
+ "as they still have jets: %s",
+ templates_with_jets.mapped("display_name"),
+ )
+ )
+ templates_with_installed_servers = self.filtered(lambda t: t.server_ids)
+ if templates_with_installed_servers:
+ raise ValidationError(
+ _(
+ "Following templates cannot be deleted "
+ "as they are installed on servers: %s",
+ ",".join(templates_with_installed_servers.mapped("display_name")),
+ )
+ )
+
+ files = self.file_ids
+ res = super().unlink()
+
+ # Unlink files only after the records are deleted
+ # This is done to avoid deleting the files while
+ # the 'unlink' method fails due to some reason.
+ if files:
+ files.unlink()
+ return res
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Odoo Actions
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ def action_install_on_servers(self):
+ """Action to install the Jet Template on the selected servers."""
+ self.ensure_one()
+ # Open the wizard to install the template on the selected servers
+ return {
+ "type": "ir.actions.act_window",
+ "name": _("Install on Servers"),
+ "res_model": "cx.tower.jet.template.install.wiz",
+ "view_mode": "form",
+ "target": "new",
+ "context": {
+ "default_jet_template_id": self.id,
+ },
+ }
+
+ def action_uninstall_from_server(self, server=None):
+ """Action to uninstall the Jet Template from the selected servers."""
+ self.ensure_one()
+ # Open the wizard to uninstall the template from the selected servers
+ if not server:
+ server_id = self.env.context.get("server_id")
+ server = self.env["cx.tower.server"].browse(server_id)
+ if not server:
+ raise ValidationError(_("No server selected"))
+ return self.uninstall_from_servers(servers=server)
+
+ def action_open_command_logs(self):
+ """
+ Open current server command log records
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.action_cx_tower_command_log"
+ )
+ action["domain"] = [("jet_template_id", "=", self.id)] # pylint: disable=no-member
+ return action
+
+ def action_open_plan_logs(self):
+ """
+ Open current server flightplan log records
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.action_cx_tower_plan_log"
+ )
+ action["domain"] = [("jet_template_id", "=", self.id)] # pylint: disable=no-member
+ return action
+
+ def action_open_files(self):
+ """
+ Open files of the current server
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_file_action"
+ )
+ action["domain"] = [("jet_template_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_jet_template_id": self.id, # pylint: disable=no-member
+ "search_default_group_by_jet_id": 1,
+ }
+ )
+ action["context"] = context
+ return action
+
+ def action_open_jets(self):
+ """
+ Open jets of the current jet template
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_action"
+ )
+ 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_jet_template_id": self.id, # pylint: disable=no-member
+ "group_by": "server_id",
+ }
+ )
+ action["domain"] = [("jet_template_id", "=", self.id)] # pylint: disable=no-member
+ action["context"] = context
+ return action
+
+ def action_new_jet(self):
+ """
+ Returns wizard action to launch a jet
+ """
+ context = self.env.context.copy()
+ context.update(
+ {
+ "default_jet_template_id": self.id
+ if self.show_in_create_wizard
+ else False,
+ }
+ )
+ return {
+ "type": "ir.actions.act_window",
+ "name": _("Launch New Jet"),
+ "res_model": "cx.tower.jet.create.wizard",
+ "view_mode": "form",
+ "target": "new",
+ "context": context,
+ }
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # General functions
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ def get_variable_value(self, variable_reference, no_fallback=False):
+ """
+ Return the value of a variable for the current jet.
+ NB: this function follows the value application order.
+ Jet Template->Server->Global
+ Args:
+ variable_reference (Char): The reference of the variable
+ to get the value for
+ no_fallback (bool): If True, will return current record value
+ without checking fallback values.
+
+
+ Returns:
+ str: The value of the variable for the current record or None
+ """
+ self.ensure_one()
+ if no_fallback:
+ return super().get_variable_value(variable_reference, no_fallback)
+ variable = self.env["cx.tower.variable"].get_by_reference(variable_reference)
+ if not variable:
+ return None
+ values = variable._get_variable_values_by_references(
+ variable_references=[variable_reference], jet_template=self
+ )
+ return values[variable_reference]
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Template Actions
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ def _get_action_path(self, state_from=None, state_to=None):
+ """Return the order of actions that lead from one state to another.
+ If the initial state is not provided, must start with "Create Action".
+ If the final state is not provided, must end with "Destroy Action".
+
+ Args:
+ state_from (cx.tower.jet.state()): State to start from
+ state_to (cx.tower.jet.state()): State to end at
+
+ Returns:
+ list: List of actions that lead from one state to another
+ """
+ self.ensure_one()
+
+ original_state_to = state_to
+ path = []
+
+ create_action = self.action_create_id if self.action_create_id else False
+ destroy_action = self.action_destroy_id if self.action_destroy_id else False
+
+ if not state_from:
+ if not create_action:
+ return []
+ path.append(create_action)
+ state_from = create_action.state_to_id
+
+ if not state_to:
+ if not destroy_action:
+ return []
+ state_to = destroy_action.state_from_id
+
+ if state_from == state_to:
+ if not original_state_to and destroy_action:
+ return path + [destroy_action]
+ return path
+
+ adjacency = self._get_action_adjacency()
+ state_path = self._find_action_path_bfs(state_from, state_to, adjacency)
+ if state_path is not None:
+ result_path = path + state_path
+ if not original_state_to and destroy_action:
+ result_path.append(destroy_action)
+ return result_path
+
+ if (
+ not original_state_to
+ and destroy_action
+ and state_from == destroy_action.state_from_id
+ ):
+ return path + [destroy_action]
+
+ return []
+
+ def _get_action_adjacency(self):
+ """Build adjacency list for state transitions."""
+ adjacency = {}
+ for action in self.action_ids:
+ if action.state_from_id and action.state_to_id:
+ if action.state_from_id not in adjacency:
+ adjacency[action.state_from_id] = []
+ adjacency[action.state_from_id].append((action.state_to_id, action))
+ return adjacency
+
+ def _find_action_path_bfs(self, state_from, state_to, adjacency):
+ """Find the shortest path of actions from state_from to state_to
+ using BFS.
+
+ Args:
+ state_from (cx.tower.jet.state()): State to start from
+ state_to (cx.tower.jet.state()): State to end at
+ adjacency (dict): Adjacency list for state transitions
+ """
+ queue = [(state_from, [])]
+ visited = {state_from}
+ while queue:
+ current_state, state_path = queue.pop(0)
+ if current_state not in adjacency:
+ continue
+ for next_state, action in adjacency[current_state]:
+ if next_state == state_to:
+ return state_path + [action]
+ if next_state not in visited:
+ visited.add(next_state)
+ queue.append((next_state, state_path + [action]))
+ return None
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Install/Uninstall
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ def _is_installation_needed(self, server):
+ """Check if installation is needed for the given server.
+
+ Args:
+ server: Server to check
+
+ Returns:
+ bool: False if server is already installed or being installed,
+ True otherwise
+ """
+ # Check if template is already installed on the server
+ if server.id in self.server_ids.ids:
+ return False
+
+ # Check if template is already being installed on the server
+ if (
+ server.id
+ in self.install_ids.filtered(
+ lambda install: install.jet_template_install_id.state == "processing"
+ ).server_id.ids
+ ):
+ return False
+
+ return True
+
+ def install_on_servers(self, servers):
+ """Install the Jet Template on the selected servers.
+
+ Args:
+ servers (cx.tower.server()): Servers to install the Jet Template on
+ """
+ self.ensure_one()
+
+ template_install_obj = self.env["cx.tower.jet.template.install"]
+ now = fields.Datetime.now()
+ context_timestamp = fields.Datetime.to_string(now)
+
+ for server in servers:
+ # Check if installation is needed for this server
+ if not self._is_installation_needed(server):
+ _logger.info(
+ "Template '%s' is already installed or being installed"
+ " on the server '%s'",
+ self.name, # pylint: disable=no-member
+ server.name,
+ )
+ # Notify the user
+ self.env.user.notify_info(
+ title=self.name, # pylint: disable=no-member
+ message=_(
+ "%(timestamp)s Template is already installed "
+ "or being installed"
+ " on the server '%(server_name)s'",
+ timestamp=context_timestamp,
+ server_name=server.name,
+ ),
+ )
+ continue
+
+ template_install_obj.install(
+ template=self,
+ server=server,
+ )
+
+ # Refresh the frontend views
+ self.env.user.reload_views(model="cx.tower.jet.template", rec_ids=[self.id])
+
+ def uninstall_from_servers(self, servers, raise_if_not_possible=True):
+ """Uninstall the Jet Template from the selected servers.
+
+ Args:
+ servers (cx.tower.server()): Servers to uninstall the Jet Template from
+ raise_if_not_possible (bool):
+ If True, will raise an error if the uninstallation is not possible.
+ """
+ self.ensure_one()
+ template_install_obj = self.env["cx.tower.jet.template.install"]
+
+ for server in servers:
+ # Check if installation is possible for this server
+ warning_message = None
+ # Template is not installed on the server
+ if server.id not in self.server_ids.ids:
+ warning_message = _(
+ "Template '%(template_name)s' is not installed "
+ "on the server '%(server_name)s'",
+ template_name=self.name, # pylint: disable=no-member
+ server_name=server.name,
+ )
+ # There are still jets on the server
+ elif server.jet_ids.filtered(lambda jet: jet.jet_template_id == self):
+ warning_message = _(
+ "There are still jets of template '%(template_name)s' "
+ "on the server '%(server_name)s'",
+ template_name=self.name, # pylint: disable=no-member
+ server_name=server.name,
+ )
+ # There are other templates that depend on this template
+ # installed on the server
+ elif server.jet_template_ids.filtered(
+ lambda template: template.template_requires_ids.filtered(
+ lambda dependency: dependency.template_required_id == self
+ )
+ ):
+ warning_message = _(
+ "There are other templates that depend "
+ "on template '%(template_name)s' "
+ "that are installed on the server '%(server_name)s'",
+ template_name=self.name, # pylint: disable=no-member
+ server_name=server.name,
+ )
+
+ if warning_message:
+ if raise_if_not_possible:
+ raise ValidationError(warning_message)
+ self.env.user.notify_warning(
+ message=warning_message,
+ title=self.name, # pylint: disable=no-member
+ )
+ continue
+
+ template_install_obj.uninstall(
+ template=self,
+ server=server,
+ )
+
+ def _get_system_variable_value(self, variable_reference):
+ """Return the jet template variable values
+
+ Args:
+ variable_reference (Char): variable value
+
+ Returns:
+ dict(): populates `tower` variable with with values.
+ {
+ 'jet_template': {..jet template vals..},
+ }.
+ """
+
+ # This works for a single record only!
+ self.ensure_one()
+
+ variable_value = {}
+ if variable_reference == "tower":
+ variable_value.update(
+ {
+ "jet_template": {
+ "name": self.name, # pylint: disable=no-member
+ "reference": self.reference, # pylint: disable=no-member
+ },
+ }
+ )
+ return variable_value
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Jet creation
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ def create_jet(self, server, name=None, state=None, **kwargs):
+ """
+ Create a new jet from this template on the given server.
+
+ Args:
+ server (cx.tower.server()): The server to use
+ name (str): The name of the jet.
+ If not provided, a random name will be generated.
+ Defaults to None.
+ state (cx.tower.jet.state()): The state to set the jet to.
+ If not provided, the jet will be created in the initial state.
+ Defaults to None.
+ Kwargs:
+ field values to populate in the new jet record.
+ NB: configuration variables are provided as follows:
+ variable_values (dict): Custom configuration variables
+ in the format of `{variable_reference: variable_value}`
+ eg `{'odoo_version': '16.0'}`
+ Returns:
+ cx.tower.jet(): The new jet or False if the creation has failed
+ """
+ self.ensure_one()
+
+ # Check if the jet creation is allowed on the given server
+ if not self._allow_jet_creation(server):
+ return False
+
+ # Prepare the jet values
+ vals = self._prepare_jet_values(server, name, **kwargs)
+
+ # Create a new jet
+ jet = self.env["cx.tower.jet"].create(vals)
+
+ # Set the state of the jet
+ if state:
+ jet._bring_to_state(state)
+
+ return jet
+
+ def _prepare_jet_values(self, server, name=None, **kwargs):
+ """
+ Prepare the jet values to create a new jet based
+ on the given server and template.
+
+ Args:
+ server (cx.tower.server()): The server to create the jet on
+ **kwargs: Additional values to update in the final jet record.
+ """
+ self.ensure_one()
+
+ # Check if the URL is valid
+ url = kwargs.pop("url", None)
+ if url and not is_valid_url(url, no_scheme_check=True):
+ raise ValidationError(
+ _(
+ "Invalid URL: '%(url)s'. URL must contain a protocol and "
+ "a proper domain or IP, eg 'https://my_tower_jet.example.com'",
+ url=url,
+ )
+ )
+
+ # If no name is provided, generate a random one
+ if not name:
+ name = self._generate_jet_name()
+
+ # Check if the same name already exists on the server
+ # Keep generating a new name until a unique one is found
+ jet_obj = self.env["cx.tower.jet"]
+ # Pre-fetch existing names for this server
+ existing_names = set(
+ jet_obj.search([("server_id", "=", server.id)]).mapped("name")
+ )
+
+ for _attempt in range(MAX_JET_NAME_RETRIES):
+ if name not in existing_names:
+ break
+ name = self._generate_jet_name()
+ else:
+ # Loop exhausted without finding unique name
+ raise ValidationError(
+ _(
+ "Failed to generate unique jet name after %(attempts)d attempts",
+ attempts=MAX_JET_NAME_RETRIES,
+ )
+ )
+
+ # Prepare the Jet values
+ vals = {
+ "name": name,
+ "jet_template_id": self.id, # pylint: disable=no-member
+ "server_id": server.id,
+ "url": url,
+ }
+
+ # Parse specific fields from kwargs
+ if kwargs:
+ # Parse configuration variables
+ configuration_variables = kwargs.pop("variable_values", {})
+ if configuration_variables:
+ variable_obj = self.env["cx.tower.variable"]
+ variable_values = []
+ for (
+ variable_reference,
+ variable_value,
+ ) in configuration_variables.items():
+ variable = variable_obj.get_by_reference(variable_reference)
+ if variable:
+ variable_values.append(
+ (
+ 0,
+ 0,
+ {
+ "variable_id": variable.id,
+ "value_char": variable_value,
+ },
+ )
+ )
+ continue
+ _logger.warning(
+ "Variable reference '%s' not found while creating jet '%s'",
+ variable_reference,
+ self.name, # pylint: disable=no-member
+ )
+
+ if variable_values:
+ vals.update(
+ {
+ "variable_value_ids": variable_values,
+ }
+ )
+
+ # Populate the allowed fields
+ for field in self._allowed_jet_fields():
+ if field in kwargs:
+ vals[field] = kwargs.pop(field)
+
+ return vals
+
+ def _allowed_jet_fields(self):
+ """Return the allowed fields for the jet creation"""
+ self.ensure_one()
+ return [
+ "name",
+ "reference",
+ "sequence",
+ "tag_ids",
+ "partner_id",
+ "jet_cloned_from_id",
+ "scheduled_task_ids",
+ "server_log_ids",
+ ]
+
+ def _allow_jet_creation(self, server):
+ """
+ Check if the jet creation is allowed on the given server.
+ This function can be extended to check for other conditions.
+ Eg if jet capacity is reached for the server.
+ Or server template has a certain limit on the number of jets per server.
+
+ Args:
+ server (cx.tower.server()): The server to check
+
+ Returns:
+ bool: True if the jet creation is allowed, False otherwise
+ """
+ self.ensure_one()
+ return True
+
+ def _generate_jet_name(self):
+ """Generate a unique name for a jet"""
+ self.ensure_one()
+ return (
+ f"{self.name} "
+ f"[{generate_random_id(sections=2, population=4, separator='-')}]"
+ )
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Dependency Graph
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ def _build_dependency_graph(self):
+ """Build a dependency graph of all templates this template depends on
+
+ Returns:
+ dict: A dictionary representing the dependency graph where:
+ - Keys are template IDs
+ - Values are dictionaries containing template info
+ and dependencies
+ """
+ self.ensure_one()
+
+ graph = {}
+ visited = set()
+
+ # Use a stack to process templates iteratively instead of recursion
+ stack = [self]
+
+ while stack:
+ template = stack.pop()
+
+ # Skip if already visited
+ if template.id in visited:
+ continue
+
+ # Mark as visited
+ visited.add(template.id)
+
+ # Add current template to graph
+ graph[template.id] = {
+ "template": template,
+ "name": template.name,
+ "reference": template.reference,
+ "dependencies": [],
+ "level": 0, # Will be calculated later
+ }
+
+ # Add dependencies
+ for dependency in template.template_requires_ids:
+ required_template = dependency.template_required_id
+
+ # Add dependency info
+ dep_info = {
+ "template_id": required_template.id,
+ "template_name": required_template.name,
+ "template_reference": required_template.reference,
+ "required_state_id": dependency.state_required_id.id
+ if dependency.state_required_id
+ else None,
+ "required_state_name": dependency.state_required_id.name
+ if dependency.state_required_id
+ else None,
+ }
+
+ graph[template.id]["dependencies"].append(dep_info)
+
+ # Add required template to stack if not yet visited
+ if required_template.id not in visited:
+ stack.append(required_template)
+
+ # Calculate dependency levels (distance from root template)
+ self._calculate_dependency_levels(graph)
+
+ return graph
+
+ def _calculate_dependency_levels(self, graph):
+ """Calculate the dependency level for each template in the graph
+
+ Args:
+ graph (dict): The dependency graph to update with levels
+ """
+ # Start with the root template (current template) at level 0
+ queue = [(self.id, 0)]
+ levels = {self.id: 0}
+
+ while queue:
+ template_id, level = queue.pop(0)
+
+ if template_id not in graph:
+ continue
+
+ # Update the level in the graph
+ graph[template_id]["level"] = level
+
+ # Process dependencies
+ for dep in graph[template_id]["dependencies"]:
+ dep_template_id = dep["template_id"]
+ new_level = level + 1
+
+ # Only update if we haven't seen this template
+ # or found a shorter path
+ if dep_template_id not in levels or levels[dep_template_id] > new_level:
+ levels[dep_template_id] = new_level
+ queue.append((dep_template_id, new_level))
+
+ def _topological_sort_dependency_graph(self, graph):
+ """Topological order: prerequisite templates before dependents.
+
+ For each edge ``required -> dependent`` (``dependent`` lists ``required``
+ in ``template_requires_ids``), ``required`` appears earlier in the result.
+
+ Tie-break: smallest template id first (deterministic).
+
+ Args:
+ graph (dict): Output of :meth:`_build_dependency_graph`.
+
+ Returns:
+ list: Template ids in topological order, or empty list if the graph
+ has a cycle.
+ """
+ adj = defaultdict(list)
+ indegree = {tid: 0 for tid in graph}
+
+ for tid in graph:
+ for dep in graph[tid]["dependencies"]:
+ dep_id = dep["template_id"]
+ if dep_id not in graph:
+ continue
+ adj[dep_id].append(tid)
+ indegree[tid] += 1
+
+ heap = [tid for tid in graph if indegree[tid] == 0]
+ heapq.heapify(heap)
+
+ topo = []
+ while heap:
+ node = heapq.heappop(heap)
+ topo.append(node)
+ for succ in sorted(adj[node]):
+ indegree[succ] -= 1
+ if indegree[succ] == 0:
+ heapq.heappush(heap, succ)
+
+ if len(topo) != len(graph):
+ return []
+
+ return topo
+
+ def _get_all_dependencies_level_fallback(self, graph):
+ """Fallback order when the dependency graph has a cycle: sort by level."""
+ dependencies_with_levels = []
+ for template_id, info in graph.items():
+ if template_id != self.id:
+ dependencies_with_levels.append((info["template"], info["level"]))
+
+ dependencies_with_levels.sort(key=lambda x: x[1])
+ return [t for t, _level in dependencies_with_levels]
+
+ def _get_all_dependencies(self):
+ """Get all templates that this template depends on (directly or indirectly).
+
+ Order is **reverse topological**
+ (see :meth:`_topological_sort_dependency_graph`):
+ ``cx.tower.jet.template.install`` assigns increasing ``order`` and runs
+ tasks with highest ``order`` first, so prerequisites must appear **later**
+ in this list than templates that depend on them.
+
+ Returns:
+ list: ``cx.tower.jet.template`` records excluding ``self``.
+ """
+ self.ensure_one()
+ graph = self._build_dependency_graph()
+
+ topo_order = self._topological_sort_dependency_graph(graph)
+ if not topo_order:
+ _logger.warning(
+ "Dependency cycle or invalid graph for template %s; "
+ "using level-based dependency order",
+ self.name,
+ )
+ return self._get_all_dependencies_level_fallback(graph)
+
+ dependencies = []
+ for tid in reversed(topo_order):
+ if tid == self.id:
+ continue
+ dependencies.append(graph[tid]["template"])
+
+ return dependencies
+
+ def _check_dependency_satisfaction(self, server):
+ """Check if all dependant templates are installed on the server.
+
+ Args:
+ server (cx.tower.server()): Server to check dependencies for
+
+ Returns:
+ list: Templates that are not installed on the server
+ """
+ dependencies = self._get_all_dependencies()
+
+ missing_templates = []
+
+ for dependency in dependencies:
+ if server and server.id not in dependency.server_ids.ids:
+ missing_templates.append(dependency)
+
+ return missing_templates
+
+ def _get_all_depend_on_this(self):
+ """Get all templates that depend on this template (directly or indirectly)
+
+ Returns:
+ recordset: All templates that depend on this template
+ """
+ self.ensure_one()
+
+ # Find all templates that have this template as a dependency
+ dependent_templates = set()
+
+ # Start with direct dependents
+ direct_dependents = self.env["cx.tower.jet.template"].search(
+ [("template_requires_ids.template_required_id", "=", self.id)]
+ )
+
+ # Use a queue to find indirect dependents
+ queue = list(direct_dependents)
+ processed = set()
+
+ while queue:
+ current_template = queue.pop(0)
+
+ if current_template.id in processed:
+ continue
+
+ processed.add(current_template.id)
+ dependent_templates.add(current_template.id)
+
+ # Find templates that depend on the current template
+ next_level_dependents = self.env["cx.tower.jet.template"].search(
+ [
+ (
+ "template_requires_ids.template_required_id",
+ "=",
+ current_template.id,
+ )
+ ]
+ )
+
+ for template in next_level_dependents:
+ if template.id not in processed:
+ queue.append(template)
+
+ return self.env["cx.tower.jet.template"].browse(list(dependent_templates))
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # SVG Graph Generation
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ def _generate_svg_graph(self, graph_data):
+ """Generate SVG dependency graph
+
+ Args:
+ graph_data (dict): Dictionary containing template dependency information
+
+ Returns:
+ bytes: Base64 encoded SVG content
+ """
+ width, height = 800, 600
+
+ # Create SVG root
+ svg = ET.Element(
+ "svg",
+ {
+ "width": str(width),
+ "height": str(height),
+ "xmlns": "http://www.w3.org/2000/svg",
+ "viewBox": f"0 0 {width} {height}",
+ },
+ )
+
+ # Add styles
+ style = ET.SubElement(svg, "style")
+ style.text = """
+ .node { stroke: #333; stroke-width: 2; }
+ .edge { stroke: #666; stroke-width: 2; marker-end: url(#arrowhead); }
+ .text { font-family: Arial; font-size: 14px; text-anchor: middle; font-weight: bold; }
+ .edge-label { font-family: Arial; font-size: 12px; text-anchor: middle; fill: #444; }
+ .root-node { fill: lightblue; }
+ .direct-dep { fill: lightgreen; }
+ .indirect-dep { fill: lightyellow; }
+ """ # noqa: E501
+
+ # Add arrow marker
+ defs = ET.SubElement(svg, "defs")
+ marker = ET.SubElement(
+ defs,
+ "marker",
+ {
+ "id": "arrowhead",
+ "markerWidth": "10",
+ "markerHeight": "7",
+ "refX": "9",
+ "refY": "3.5",
+ "orient": "auto",
+ },
+ )
+ ET.SubElement(marker, "polygon", {"points": "0 0, 10 3.5, 0 7", "fill": "#666"})
+
+ if not graph_data or len(graph_data) <= 1:
+ # Single node
+ self._add_single_node_svg(svg, width, height)
+ else:
+ # Multiple nodes - arrange in levels
+ self._add_multi_node_svg(svg, graph_data, width, height)
+
+ # Convert to string and then to base64
+ svg_string = ET.tostring(svg, encoding="unicode")
+ return base64.b64encode(svg_string.encode("utf-8"))
+
+ def _add_single_node_svg(self, svg, width, height):
+ """Add a single node to the SVG for templates with no dependencies
+
+ Args:
+ svg (xml.etree.ElementTree.Element): SVG root element
+ width (int): SVG width
+ height (int): SVG height
+ """
+ node_width, node_height = 200, 60
+ x = width // 2 - node_width // 2
+ y = height // 2 - node_height // 2
+
+ # Draw node rectangle
+ ET.SubElement(
+ svg,
+ "rect",
+ {
+ "x": str(x),
+ "y": str(y),
+ "width": str(node_width),
+ "height": str(node_height),
+ "class": "node root-node",
+ "rx": "10", # Rounded corners
+ },
+ )
+
+ # Add text
+ ET.SubElement(
+ svg,
+ "text",
+ {"x": str(width // 2), "y": str(height // 2 + 5), "class": "text"},
+ ).text = self.name
+
+ def _add_multi_node_svg(self, svg, graph_data, width, height):
+ """Add multiple nodes and edges to the SVG for complex dependency graphs
+
+ Args:
+ svg (xml.etree.ElementTree.Element): SVG root element
+ graph_data (dict): Dictionary containing template dependency information
+ width (int): SVG width
+ height (int): SVG height
+ """
+ # Group templates by level
+ levels = {}
+ for template_id, info in graph_data.items():
+ level = info["level"]
+ if level not in levels:
+ levels[level] = []
+ levels[level].append((template_id, info))
+
+ positions = {}
+ node_width = 180
+ node_height = 60
+ level_height = 120
+ margin = 50
+
+ # Calculate positions for each node
+ for level, nodes in levels.items():
+ y = margin + level * level_height
+ available_width = width - 2 * margin
+
+ if len(nodes) == 1:
+ # Center single node
+ x = width // 2
+ positions[nodes[0][0]] = (x, y)
+ else:
+ # Distribute multiple nodes
+ spacing = available_width / len(nodes)
+ for i, node_tuple in enumerate(nodes):
+ template_id = node_tuple[0] # Extract template_id from tuple
+ x = margin + spacing * (i + 0.5)
+ positions[template_id] = (x, y)
+
+ # Draw edges first (so they appear behind nodes)
+ self._draw_svg_edges(svg, graph_data, positions, node_height)
+
+ # Draw nodes
+ self._draw_svg_nodes(svg, graph_data, positions, node_width, node_height)
+
+ def _draw_svg_edges(self, svg, graph_data, positions, node_height):
+ """Draw edges between nodes in the SVG
+
+ Args:
+ svg (xml.etree.ElementTree.Element): SVG root element
+ graph_data (dict): Dictionary containing template dependency information
+ positions (dict): Dictionary mapping template IDs to (x, y) positions
+ node_height (int): Height of nodes for edge positioning
+ """
+ for template_id, info in graph_data.items():
+ if template_id in positions:
+ x1, y1 = positions[template_id]
+
+ for dep in info["dependencies"]:
+ dep_id = dep["template_id"]
+ if dep_id in positions:
+ x2, y2 = positions[dep_id]
+
+ # Draw edge line
+ ET.SubElement(
+ svg,
+ "line",
+ {
+ "x1": str(x1),
+ "y1": str(y1 + node_height // 2),
+ "x2": str(x2),
+ "y2": str(y2 - node_height // 2),
+ "class": "edge",
+ },
+ )
+
+ # Add edge label if there's a required state
+ if dep["required_state_name"]:
+ mid_x = (x1 + x2) / 2
+ mid_y = (y1 + y2) / 2
+
+ # Background rectangle for label
+ label_text = dep["required_state_name"]
+ label_width = len(label_text) * 8 + 10
+ label_height = 20
+
+ ET.SubElement(
+ svg,
+ "rect",
+ {
+ "x": str(mid_x - label_width // 2),
+ "y": str(mid_y - label_height // 2),
+ "width": str(label_width),
+ "height": str(label_height),
+ "fill": "white",
+ "stroke": "#ccc",
+ "rx": "3",
+ },
+ )
+
+ ET.SubElement(
+ svg,
+ "text",
+ {
+ "x": str(mid_x),
+ "y": str(mid_y + 4),
+ "class": "edge-label",
+ },
+ ).text = label_text
+
+ def _draw_svg_nodes(self, svg, graph_data, positions, node_width, node_height):
+ """Draw nodes in the SVG
+
+ Args:
+ svg (xml.etree.ElementTree.Element): SVG root element
+ graph_data (dict): Dictionary containing template dependency information
+ positions (dict): Dictionary mapping template IDs to (x, y) positions
+ node_width (int): Width of nodes
+ node_height (int): Height of nodes
+ """
+ for template_id, info in graph_data.items():
+ if template_id in positions:
+ x, y = positions[template_id]
+ template_obj = info["template"]
+
+ # Determine node class based on level
+ if info["level"] == 0:
+ node_class = "node root-node"
+ elif info["level"] == 1:
+ node_class = "node direct-dep"
+ else:
+ node_class = "node indirect-dep"
+
+ # Draw node rectangle
+ ET.SubElement(
+ svg,
+ "rect",
+ {
+ "x": str(x - node_width // 2),
+ "y": str(y - node_height // 2),
+ "width": str(node_width),
+ "height": str(node_height),
+ "class": node_class,
+ "rx": "10", # Rounded corners
+ },
+ )
+
+ # Add text (truncate if too long)
+ display_name = template_obj.name
+ if len(display_name) > 20:
+ display_name = display_name[:17] + "..."
+
+ ET.SubElement(
+ svg, "text", {"x": str(x), "y": str(y + 5), "class": "text"}
+ ).text = display_name
+
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ # Access role mixin functions
+ # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ def _get_post_create_fields(self):
+ """
+ Add fields that should be populated after jet template creation
+ """
+ res = super()._get_post_create_fields()
+ return res + ["variable_value_ids", "server_log_ids"]
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_template_dependency.py b/addons/cetmix_tower_server/models/cx_tower_jet_template_dependency.py
new file mode 100644
index 0000000..6f37dc2
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_template_dependency.py
@@ -0,0 +1,168 @@
+# 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 CxTowerJetTemplateDependency(models.Model):
+ """Define dependencies between Jet templates"""
+
+ _name = "cx.tower.jet.template.dependency"
+ _inherit = "cx.tower.reference.mixin"
+ _description = "Cetmix Tower Jet Template Dependency"
+ _log_access = False
+
+ name = fields.Char(related="template_id.name", readonly=True)
+ template_id = fields.Many2one(
+ string="Jet",
+ comodel_name="cx.tower.jet.template",
+ ondelete="cascade",
+ required=True,
+ help="The Jet template that requires another template",
+ )
+
+ template_required_id = fields.Many2one(
+ string="Required Jet",
+ comodel_name="cx.tower.jet.template",
+ ondelete="restrict",
+ required=True,
+ help="The Jet template that is required to be in a specific state",
+ domain="[('id', '!=', template_id)]",
+ )
+
+ state_required_id = fields.Many2one(
+ string="Required State",
+ comodel_name="cx.tower.jet.state",
+ required=True,
+ ondelete="restrict",
+ help="The state of the required Jet",
+ )
+
+ _sql_constraints = [
+ (
+ "unique_template_dependency",
+ "UNIQUE(template_id, template_required_id)",
+ "A template can only depend on another template once!",
+ ),
+ ]
+
+ @api.constrains(
+ "template_id",
+ "template_required_id",
+ )
+ def _check_circular_dependency(self):
+ """Check if this dependency would create a circular dependency chain"""
+ for dependency in self:
+ # Skip if the dependency isn't properly set yet
+ if not dependency.template_id or not dependency.template_required_id:
+ continue
+
+ # Self-dependency is not allowed and already prevented by domain constraints
+ if dependency.template_id == dependency.template_required_id:
+ raise ValidationError(_("A template cannot depend on itself!"))
+
+ # Build dependency graph
+ graph = self._build_dependency_graph()
+
+ # Add the new dependency edge being created
+ if dependency.template_id.id not in graph:
+ graph[dependency.template_id.id] = set()
+ graph[dependency.template_id.id].add(dependency.template_required_id.id)
+
+ # Check for circular dependencies
+ if self._has_cycle(graph, dependency.template_id.id):
+ raise ValidationError(
+ _(
+ "This dependency would create a circular reference chain! "
+ "Template '%(template)s' would indirectly depend on itself.",
+ template=dependency.template_id.name,
+ )
+ )
+
+ @api.depends("template_id", "template_required_id")
+ def _compute_display_name(self):
+ for dependency in self:
+ dependency.display_name = (
+ (
+ f"{dependency.template_id.name} ->"
+ f" {dependency.template_required_id.name}"
+ )
+ if dependency.template_id and dependency.template_required_id
+ else "..."
+ )
+
+ def write(self, vals):
+ """Do not allow modifications after creation"""
+ # Allow modifications in install mode only to load demo data
+ if ("template_id" in vals or "template_required_id" in vals) and not (
+ self._context.get("install_mode") and self._context.get("install_xmlid")
+ ):
+ raise ValidationError(
+ _(
+ "You cannot modify an existing template dependency! "
+ "Please remove it and create a new one."
+ )
+ )
+ return super().write(vals)
+
+ def _build_dependency_graph(self):
+ """Build a directed graph of template dependencies
+
+ Returns:
+ dict: A dictionary where keys are template IDs and values are
+ sets of template IDs that are required by the key template
+ """
+ graph = {}
+ # Get all dependencies in the system
+ # TODO: This is not efficient, we should find a better way later.
+ # Eg cache the graph in the template model.
+ all_deps = self.search([])
+
+ for dep in all_deps:
+ from_id = dep.template_id.id
+ to_id = dep.template_required_id.id
+
+ if from_id not in graph:
+ graph[from_id] = set()
+
+ graph[from_id].add(to_id)
+
+ # Ensure the to_id is in the graph even if it doesn't require anything
+ if to_id not in graph:
+ graph[to_id] = set()
+
+ return graph
+
+ def _has_cycle(self, graph, start_node, visited=None, path=None):
+ """Check if the graph has a cycle starting from start_node
+
+ Args:
+ graph (dict): Dependency graph where keys are template IDs and values are
+ sets of template IDs that the key depends on
+ start_node (int): Template ID to start the traversal from
+ visited (set, optional): Set of already visited nodes
+ path (set, optional): Set of nodes in the current DFS path
+
+ Returns:
+ bool: True if a cycle is detected, False otherwise
+ """
+ if visited is None:
+ visited = set()
+ if path is None:
+ path = set()
+
+ visited.add(start_node)
+ path.add(start_node)
+
+ for neighbor in graph.get(start_node, set()):
+ if neighbor not in visited:
+ if self._has_cycle(graph, neighbor, visited, path):
+ return True
+ elif neighbor in path:
+ # We found a cycle
+ return True
+
+ # Remove the current node from the path as we backtrack
+ path.remove(start_node)
+ return False
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_template_install.py b/addons/cetmix_tower_server/models/cx_tower_jet_template_install.py
new file mode 100644
index 0000000..a4f36aa
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_template_install.py
@@ -0,0 +1,474 @@
+import logging
+
+from odoo import _, api, fields, models
+
+_logger = logging.getLogger(__name__)
+
+
+class CxTowerJetTemplateInstall(models.Model):
+ """
+ Used to track installation of Jet Templates on servers.
+ """
+
+ _name = "cx.tower.jet.template.install"
+ _description = "Jet Template Install/Uninstall"
+ _order = "create_date desc"
+ _rec_name = "jet_template_id"
+
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ required=True,
+ help="Template to install/uninstall",
+ )
+ server_id = fields.Many2one(
+ comodel_name="cx.tower.server",
+ index=True,
+ ondelete="cascade",
+ required=True,
+ help="Server to install/uninstall the template on",
+ )
+ action = fields.Selection(
+ selection=[("install", "Install"), ("uninstall", "Uninstall")],
+ default="install",
+ )
+ date_done = fields.Datetime(string="Completed on", readonly=True)
+ line_ids = fields.One2many(
+ comodel_name="cx.tower.jet.template.install.line",
+ inverse_name="jet_template_install_id",
+ auto_join=True,
+ string="Templates to install",
+ help="Complete list of templates to install/uninstall including dependencies",
+ )
+ current_line_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template.install.line",
+ string="Currently Installing",
+ help="Line that is currently being installed",
+ )
+ state = fields.Selection(
+ selection=[
+ ("processing", "Processing"),
+ ("done", "Done"),
+ ("failed", "Failed"),
+ ],
+ default="processing",
+ index=True,
+ )
+
+ @api.model
+ def install(self, server, template):
+ """Install the template on the server.
+
+ Args:
+ server (cx.tower.server()): The server to install the template on.
+ template (cx.tower.jet.template()): The template to install.
+
+ Returns:
+ cx.tower.jet.template.install(): The installation record.
+ """
+ server.ensure_one()
+ template.ensure_one()
+
+ # Compose the list of templates to install
+ # NB: templates will be installed later in reverse order
+ # to ensure that dependencies are satisfied
+ template_to_process = [template] + template._check_dependency_satisfaction(
+ server
+ )
+
+ # Prepare the template install lines
+ template_to_process_lines = []
+ order = 0
+ for t in template_to_process:
+ template_to_process_lines.append(
+ (0, 0, {"jet_template_id": t.id, "order": order})
+ )
+ order += 1
+
+ # Create a new install record
+ install_record = self.create(
+ {
+ "jet_template_id": template.id,
+ "server_id": server.id,
+ "line_ids": template_to_process_lines,
+ }
+ )
+
+ # Send notification
+ # Action for button
+ action = self.env["ir.actions.act_window"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_template_install_action"
+ )
+
+ context = self.env.context.copy()
+ params = dict(context.get("params") or {})
+ params["button_name"] = _("View Installation")
+ context["params"] = params
+
+ # Add record id and context to the action
+ action.update(
+ {
+ "context": context,
+ "res_id": install_record.id,
+ "views": [(False, "form")],
+ }
+ )
+
+ self.env.user.notify_info(
+ message=_(
+ "%(timestamp)s " "Installing template on server '%(server_name)s'",
+ server_name=server.name,
+ timestamp=fields.Datetime.context_timestamp(
+ self, fields.Datetime.now()
+ ),
+ ),
+ title=template.name,
+ sticky=False, # explicitly set to False to avoid blocking the user's screen
+ action=action,
+ )
+
+ # Launch the installation
+ install_record._process_install()
+
+ # Return the installation record
+ return install_record
+
+ @api.model
+ def uninstall(self, server, template):
+ """Uninstall the template from the server.
+ NB: only one template can be uninstalled at a time.
+
+ Args:
+ server (cx.tower.server()): The server to uninstall the template from.
+ template (cx.tower.jet.template()): The template to uninstall.
+ """
+ server.ensure_one()
+ template.ensure_one()
+
+ # Create a new install record
+ install_record = self.create(
+ {
+ "jet_template_id": template.id,
+ "server_id": server.id,
+ "line_ids": [(0, 0, {"jet_template_id": template.id, "order": 0})],
+ "action": "uninstall",
+ }
+ )
+
+ # Send notification
+ # Action for button
+ action = self.env["ir.actions.act_window"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_template_install_action"
+ )
+
+ context = self.env.context.copy()
+ params = dict(context.get("params") or {})
+ params["button_name"] = _("View Installation")
+ context["params"] = params
+
+ # Add record id and context to the action
+ action.update(
+ {
+ "context": context,
+ "res_id": install_record.id,
+ "views": [(False, "form")],
+ }
+ )
+
+ self.env.user.notify_info(
+ message=_(
+ "%(timestamp)s "
+ "Uninstalling template on server '%(server_name)s'",
+ server_name=server.name,
+ timestamp=fields.Datetime.context_timestamp(
+ self, fields.Datetime.now()
+ ),
+ ),
+ title=template.name,
+ sticky=False, # explicitly set to False to avoid blocking the user's screen
+ action=action,
+ )
+
+ # Launch the installation
+ install_record._process_install()
+
+ # Return the installation record
+ return install_record
+
+ def _process_install(self):
+ """
+ Process the installation or uninstallation of the template.
+ """
+ self.ensure_one()
+
+ # We are not using `while` because flight plans
+ # may run asynchronously and we don't want to
+ # block the execution of the function
+
+ # Continue only if the job is still processing
+ if self.state != "processing":
+ return
+
+ # Exit if there are some lines currently being installed
+ if self.current_line_id:
+ return
+
+ # Get the template to install
+ installation_tasks = self.line_ids.sorted("order", reverse=True)
+ for installation_task in installation_tasks:
+ # Pick the templates only in the "To Process" state
+ if installation_task.state != "to_process":
+ continue
+
+ # Get the flight plan to install the template
+ if self.action == "install":
+ flight_plan = installation_task.jet_template_id.plan_install_id # pylint: disable=no-member
+ else:
+ flight_plan = installation_task.jet_template_id.plan_uninstall_id # pylint: disable=no-member
+
+ # Run the corresponding flight plan
+ if flight_plan:
+ # Update the current template install line
+ self.write(
+ {
+ "current_line_id": installation_task.id,
+ }
+ )
+
+ # Add the install record to the flight plan params
+ plan_params = {
+ "jet_template_install_id": self.id, # pylint: disable=no-member
+ }
+ with self.env.cr.savepoint():
+ # Run the flight plan (exceptions handled inside the flight plan)
+ self.server_id.run_flight_plan(
+ flight_plan=flight_plan,
+ jet_template=installation_task.jet_template_id,
+ **{"plan_log": plan_params},
+ )
+ # Flight plan will trigger the `_process_install` function again
+ # if the flight plan is finished successfully.
+ # So we don't need continue the loop in this case.
+ return
+
+ # Mark the installation task as "Done"
+ # because nothing else is to be done here.
+ installation_task.write(
+ {
+ "state": "done",
+ }
+ )
+ # Add to the list of installed templates
+ if self.action == "install":
+ installation_task.jet_template_id.write(
+ {"server_ids": [(4, self.server_id.id)]}
+ )
+ else:
+ installation_task.jet_template_id.write(
+ {"server_ids": [(3, self.server_id.id)]}
+ )
+
+ # Refresh the frontend views
+ self.env.user.reload_views(
+ model="cx.tower.jet.template.install",
+ rec_ids=[self.id],
+ )
+
+ # Mark the installation as done
+ now = fields.Datetime.now()
+ self.write(
+ {
+ "state": "done",
+ "date_done": now,
+ }
+ )
+
+ # Refresh the frontend views
+ self.env.user.reload_views(
+ model="cx.tower.jet.template.install", rec_ids=[self.id]
+ )
+ self.env.user.reload_views(
+ model="cx.tower.server", view_types=["form"], rec_ids=[self.server_id.id]
+ )
+ self.env.user.reload_views(
+ model="cx.tower.jet.template",
+ view_types=["form"],
+ rec_ids=[self.jet_template_id.id],
+ )
+
+ # 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"
+ )
+ # Send notification to the user
+ if notification_type_success:
+ # Action for button
+ action = self.env["ir.actions.act_window"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_template_install_action"
+ )
+
+ context = self.env.context.copy()
+ params = dict(context.get("params") or {})
+ params["button_name"] = _("View Installation")
+ context["params"] = params
+
+ # Add record id and context to the action
+ action.update(
+ {
+ "context": context,
+ "res_id": self.id,
+ "views": [(False, "form")],
+ }
+ )
+ # Send success notification
+ self.env.user.notify_success(
+ message=_(
+ "%(timestamp)s "
+ "%(action)s completed on server '%(server_name)s'",
+ action=_("Installation")
+ if self.action == "install"
+ else _("Uninstallation"),
+ server_name=self.server_id.name,
+ timestamp=fields.Datetime.context_timestamp(self, now),
+ ),
+ title=self.jet_template_id.name, # pylint: disable=no-member
+ sticky=notification_type_success == "sticky",
+ action=action,
+ )
+
+ def _flight_plan_finished(self, plan_status):
+ """
+ Triggered when a flight plan that is used for installing/uninstalling
+ a template is finished.
+
+ Args:
+ plan_status (int): The exit code of the flight plan.
+ """
+ self.ensure_one()
+
+ # Validate callback state
+ if not self.current_line_id:
+ _logger.warning(
+ "Callback invoked with no current_line_id for install %s", self.id
+ )
+ return
+
+ if self.state != "processing":
+ _logger.warning(
+ "Callback invoked for install %s in state %s", self.id, self.state
+ )
+ return
+
+ # Flight plan finished successfully
+ if plan_status == 0:
+ # Mark current line as done
+ self.current_line_id.write( # pylint: disable=no-member
+ {
+ "state": "done",
+ }
+ )
+ # Add template to the list of installed templates
+ # or remove it from the list if it is being uninstalled
+ if self.action == "install":
+ self.current_line_id.jet_template_id.write( # pylint: disable=no-member
+ {"server_ids": [(4, self.server_id.id)]}
+ )
+ else:
+ self.current_line_id.jet_template_id.write( # pylint: disable=no-member
+ {"server_ids": [(3, self.server_id.id)]}
+ )
+
+ # Remove the link to the current line and continue
+ self.write({"current_line_id": False})
+
+ # Refresh the frontend views
+ self.env.user.reload_views(
+ model="cx.tower.jet.template.install",
+ rec_ids=[self.id],
+ )
+ self._process_install()
+ else:
+ # Mark current line as failed
+ self.current_line_id.write( # pylint: disable=no-member
+ {
+ "state": "failed",
+ }
+ )
+ # Clear the current line link
+ self.write(
+ {
+ "state": "failed",
+ "date_done": fields.Datetime.now(),
+ "current_line_id": False,
+ }
+ )
+
+ # Set all other 'to_process' lines as failed
+ self.line_ids.filtered(lambda line: line.state == "to_process").write(
+ {
+ "state": "failed",
+ }
+ )
+
+ # Refresh the frontend views
+ self.env.user.reload_views(
+ model="cx.tower.jet.template.install",
+ rec_ids=[self.id],
+ )
+ # Send notification to the user
+ # Check if notifications are enabled
+ ICP_sudo = self.env["ir.config_parameter"].sudo()
+ notification_type_error = ICP_sudo.get_param(
+ "cetmix_tower_server.notification_type_error"
+ )
+ if notification_type_error:
+ # Action for button
+ action = self.env["ir.actions.act_window"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_template_install_action"
+ )
+
+ context = self.env.context.copy()
+ params = dict(context.get("params") or {})
+ params["button_name"] = _("View Installation")
+ context["params"] = params
+
+ # Add record id and context to the action
+ action.update(
+ {
+ "context": context,
+ "res_id": self.id,
+ "views": [(False, "form")],
+ }
+ )
+ # Send error notification
+ self.env.user.notify_danger(
+ message=_(
+ "%(timestamp)s "
+ "%(action)s failed on server '%(server_name)s'",
+ action=_("Installation")
+ if self.action == "install"
+ else _("Uninstallation"),
+ server_name=self.server_id.name,
+ timestamp=fields.Datetime.context_timestamp(
+ self, fields.Datetime.now()
+ ),
+ ),
+ title=self.jet_template_id.name,
+ sticky=notification_type_error == "sticky",
+ action=action,
+ )
+
+ def action_view_flight_plan_logs(self):
+ """Open flight plan logs related to this installation"""
+ self.ensure_one()
+
+ return {
+ "name": _(
+ "Flight Plan Logs - %(install_name)s",
+ install_name=self.jet_template_id.name,
+ ),
+ "type": "ir.actions.act_window",
+ "res_model": "cx.tower.plan.log",
+ "view_mode": "list,form",
+ "domain": [("jet_template_install_id", "=", self.id)], # pylint: disable=no-member
+ }
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_template_install_line.py b/addons/cetmix_tower_server/models/cx_tower_jet_template_install_line.py
new file mode 100644
index 0000000..fc41d3d
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_template_install_line.py
@@ -0,0 +1,41 @@
+from odoo import fields, models
+
+
+class CxTowerJetTemplateInstallLine(models.Model):
+ """
+ Used to track the order and status of templates to install/uninstall.
+ """
+
+ _name = "cx.tower.jet.template.install.line"
+ _description = "Jet Template Install/Uninstall Line"
+ _order = "order"
+ _rec_name = "jet_template_id"
+
+ order = fields.Integer(required=True, default=10)
+ jet_template_install_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template.install",
+ ondelete="cascade",
+ required=True,
+ index=True,
+ )
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ ondelete="cascade",
+ required=True,
+ index=True,
+ )
+ server_id = fields.Many2one(
+ comodel_name="cx.tower.server",
+ related="jet_template_install_id.server_id",
+ readonly=True,
+ store=True,
+ )
+ state = fields.Selection(
+ selection=[
+ ("to_process", "To Process"),
+ ("processing", "Processing"),
+ ("done", "Done"),
+ ("failed", "Failed"),
+ ],
+ default="to_process",
+ )
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py b/addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py
new file mode 100644
index 0000000..eeb17a5
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_waypoint.py
@@ -0,0 +1,789 @@
+# Copyright (C) 2024 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 .constants import GENERAL_ERROR, WAYPOINT_CREATE_FAILED
+from .tools import generate_random_id
+
+_logger = logging.getLogger(__name__)
+
+
+class CxTowerJetWaypoint(models.Model):
+ """Jet Waypoints represent waypoints for jets"""
+
+ _name = "cx.tower.jet.waypoint"
+ _description = "Cetmix Tower Jet Waypoint"
+ _inherit = [
+ "cx.tower.reference.mixin",
+ "cx.tower.access.mixin",
+ "cx.tower.metadata.mixin",
+ ]
+ _order = "create_date desc"
+
+ name = fields.Char(required=True)
+ access_level = fields.Selection(
+ selection=lambda self: self.env[
+ "cx.tower.jet.waypoint.template"
+ ]._selection_access_level(),
+ compute="_compute_access_level",
+ readonly=False,
+ store=True,
+ )
+ state = fields.Selection(
+ selection=[
+ ("draft", "Draft"),
+ ("preparing", "Preparing"),
+ ("ready", "Ready"),
+ ("error", "Error"),
+ ("arriving", "Arriving"),
+ ("leaving", "Leaving"),
+ ("current", "Current"),
+ ("deleting", "Deleting"),
+ ("deleted", "Deleted"),
+ ],
+ default="draft",
+ required=True,
+ readonly=True,
+ )
+ can_fly_to = fields.Boolean(
+ compute="_compute_can_fly_to",
+ readonly=True,
+ )
+ is_destination = fields.Boolean(
+ help="Indicates if this waypoint is the current destination",
+ )
+ jet_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ required=True,
+ ondelete="cascade",
+ help="Jet this waypoint belongs to",
+ )
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ related="jet_id.jet_template_id",
+ readonly=True,
+ )
+ waypoint_template_id = fields.Many2one(
+ string="Type",
+ comodel_name="cx.tower.jet.waypoint.template",
+ help="Waypoint template this waypoint is based on",
+ domain="[('jet_template_id', '=', jet_template_id)]",
+ required=True,
+ ondelete="restrict",
+ )
+ variable_values = fields.Json(
+ help="Custom variable values for this waypoint",
+ readonly=True,
+ )
+ variable_values_text = fields.Text(
+ help="Custom variable values for this waypoint",
+ compute="_compute_variable_values_text",
+ )
+ created_from_command_log_id = fields.Many2one(
+ comodel_name="cx.tower.command.log",
+ string="Created From",
+ help="Command log that created this waypoint; the waypoint callback "
+ "finishes it when the waypoint reaches ready/current or error. "
+ "Kept for debugging/audit.",
+ ondelete="set null",
+ copy=False,
+ )
+
+ # ------------------------------------
+ # --------- Selection ------------
+ # ------------------------------------
+ def _selection_access_level(self):
+ """
+ Available access levels
+
+ Returns:
+ List of tuples: available options.
+ """
+ return [
+ ("2", "Manager"),
+ ("3", "Root"),
+ ]
+
+ # ------------------------------------
+ # --------- Computed Fields ---------
+ # ------------------------------------
+ @api.depends("name", "create_date")
+ def _compute_display_name(self):
+ """
+ Compute the display name of the waypoint
+ """
+ for waypoint in self:
+ timestamp = fields.Datetime.context_timestamp(
+ waypoint, waypoint.create_date
+ )
+ formatted_date = timestamp.strftime("%Y-%m-%d %H:%M:%S")
+ waypoint.display_name = f"{waypoint.name} ({formatted_date})"
+
+ @api.depends("waypoint_template_id")
+ def _compute_access_level(self):
+ """
+ Set default access level to the waypoint template access level
+ """
+ for waypoint in self:
+ if waypoint.waypoint_template_id:
+ waypoint.access_level = waypoint.waypoint_template_id.access_level
+
+ @api.depends("jet_id.waypoint_ids", "jet_id.waypoint_ids.state")
+ def _compute_can_fly_to(self):
+ """
+ Can fly only if waypoint is in the ready state and
+ is not the current waypoint and all the jet waypoints
+ are in the "ready" state
+ """
+ for waypoint in self:
+ all_waypoints = waypoint.jet_id.waypoint_ids
+ waypoint.can_fly_to = waypoint.state == "ready" and not bool(
+ all_waypoints.filtered(
+ lambda w: w.state not in ["ready", "error", "current"]
+ )
+ )
+
+ @api.depends("variable_values")
+ def _compute_variable_values_text(self):
+ """
+ Compute the variable values text for the waypoint
+ """
+ for waypoint in self:
+ waypoint.variable_values_text = (
+ str(waypoint.variable_values) if waypoint.variable_values else False
+ )
+
+ # ------------------------------------
+ # --------- Constraints -------------
+ # ------------------------------------
+ @api.constrains("is_destination", "jet_id")
+ def _check_is_destination(self):
+ """
+ Validate ``is_destination`` on each waypoint in the recordset.
+
+ Raises a ValidationError when:
+ - The waypoint is being set as destination while in the ``draft``,
+ ``error``, ``leaving``, ``deleting``, or ``deleted`` state.
+ Use ``prepare(is_destination=True)`` to designate a destination
+ waypoint; it transitions the waypoint out of ``draft`` and sets
+ ``is_destination`` atomically.
+ - Another destination waypoint already exists for the same jet
+ (at most one destination per jet is allowed).
+ """
+ destination_waypoints = self.filtered("is_destination")
+ if not destination_waypoints:
+ return
+
+ existing_destinations = self.search(
+ [
+ ("jet_id", "in", destination_waypoints.mapped("jet_id").ids),
+ ("is_destination", "=", True),
+ ("id", "not in", destination_waypoints.ids),
+ ]
+ )
+ existing_by_jet = {wp.jet_id.id: wp for wp in existing_destinations}
+
+ # Track jet IDs already claimed as destination within this batch so that
+ # two records in the same transaction are caught even though neither
+ # appears in the DB search above.
+ seen_in_batch = {}
+
+ invalid_states = {"draft", "error", "leaving", "deleting", "deleted"}
+
+ for waypoint in destination_waypoints:
+ if waypoint.state in invalid_states:
+ raise ValidationError(
+ _(
+ "Cannot set is_destination to True for waypoint %(waypoint)s "
+ "because it is in the %(state)s state",
+ waypoint=waypoint.name,
+ state=waypoint.state,
+ )
+ )
+ jet_id = waypoint.jet_id.id
+ duplicate = existing_by_jet.get(jet_id) or seen_in_batch.get(jet_id)
+ if duplicate:
+ raise ValidationError(
+ _(
+ "Waypoint %(existing)s is already set as the destination "
+ "for jet %(jet)s. Only one destination waypoint is allowed "
+ "per jet.",
+ existing=duplicate.name,
+ jet=waypoint.jet_id.name,
+ )
+ )
+ seen_in_batch[jet_id] = waypoint
+
+ # ------------------------------------
+ # --------- CRUD Methods -------------
+ # ------------------------------------
+ @api.model_create_multi
+ def create(self, vals_list):
+ """
+ Create waypoints
+ - Generate waypoint reference if not provided
+ """
+
+ for vals in vals_list:
+ if not vals.get("reference"):
+ vals["reference"] = generate_random_id(
+ sections=4, population=4, separator="_"
+ )
+ jets = super().create(vals_list)
+ return jets
+
+ def write(self, vals):
+ """
+ Write. Do not allow to modify the template
+ if the waypoint is not in the draft state
+ """
+ if "waypoint_template_id" in vals and not vals.get("state") == "draft":
+ for waypoint in self:
+ if (
+ waypoint.waypoint_template_id.id != vals.get("waypoint_template_id")
+ and waypoint.state != "draft"
+ ):
+ raise ValidationError(
+ _(
+ "Cannot change waypoint type for %(waypoint)s "
+ "because it is not in the draft state",
+ waypoint=waypoint.name,
+ )
+ )
+ # Invalidate the state field
+ fields_to_invalidate = []
+ if "state" in vals:
+ fields_to_invalidate.append("state")
+ if "variable_values" in vals:
+ fields_to_invalidate.append("variable_values")
+ if "is_destination" in vals:
+ fields_to_invalidate.append("is_destination")
+ if fields_to_invalidate:
+ self.invalidate_recordset(fields_to_invalidate)
+ return super().write(vals)
+
+ def unlink(self):
+ """
+ Unlink.
+
+ Raises:
+ ValidationError: If the waypoint cannot be deleted
+ set the context value 'waypoint_no_raise_on_delete' to True
+ for not to raise the exception.
+ """
+ # Deletable waypoints:
+ # - are in the 'draft' or 'deleted' state
+ # - waypoint is in the 'ready' or 'error' state and template
+ # doesn't have on_delete flight plan
+ # Non-deletable waypoints:
+ # - are in the 'arriving', 'leaving' or 'preparing' state
+ # or is the current waypoint of the jet
+ # or is marked as the active destination (is_destination=True)
+ # Need to run the on_delete flight plan:
+ # - waypoint is in the 'ready' or 'error' state and template has
+ # on_delete flight plan
+ if self._context.get("waypoint_force_delete"):
+ return super().unlink()
+
+ waypoints_to_delete = self.browse()
+ waypoints_to_run_delete_plan = self.browse()
+ for waypoint in self:
+ if waypoint.is_destination:
+ exception_message = _(
+ "Cannot delete waypoint %(waypoint)s because it is "
+ "currently designated as the destination for jet %(jet)s.",
+ waypoint=waypoint.name,
+ jet=waypoint.jet_id.name,
+ )
+ if self._context.get("waypoint_no_raise_on_delete"):
+ _logger.error(exception_message)
+ continue
+ raise ValidationError(exception_message)
+ if waypoint.state not in ["draft", "deleted", "error", "ready"]:
+ if waypoint.state == "current":
+ exception_message = _(
+ "Cannot delete the waypoint %(waypoint)s because it is"
+ " the current waypoint of the jet %(jet)s",
+ waypoint=waypoint.name,
+ jet=waypoint.jet_id.name,
+ )
+ else:
+ exception_message = _(
+ "Cannot delete the waypoint %(waypoint)s because it is"
+ " in the %(state)s state",
+ waypoint=waypoint.name,
+ state=waypoint.state,
+ )
+ if self._context.get("waypoint_no_raise_on_delete"):
+ _logger.error(exception_message)
+ continue
+ raise ValidationError(exception_message)
+ if (
+ waypoint.state in ["ready", "error"]
+ and waypoint.waypoint_template_id.plan_delete_id
+ ):
+ waypoints_to_run_delete_plan |= waypoint
+ continue
+ waypoints_to_delete |= waypoint
+
+ if waypoints_to_delete:
+ result = super(CxTowerJetWaypoint, waypoints_to_delete).unlink()
+ else:
+ result = True
+
+ for waypoint in waypoints_to_run_delete_plan:
+ waypoint.write({"state": "deleting"})
+ waypoint.jet_id.server_id.sudo().run_flight_plan(
+ jet=waypoint.jet_id,
+ flight_plan=waypoint.waypoint_template_id.plan_delete_id,
+ plan_log={"waypoint_id": waypoint.id},
+ variable_values=waypoint._get_custom_variable_values(),
+ )
+ return result
+
+ # ------------------------------------
+ # --------- Waypoint Setters ---------
+ # ------------------------------------
+ def prepare(self, is_destination=False):
+ """
+ Prepare the newly created waypoint.
+
+ Args:
+ is_destination (bool): True if the waypoint is the destination
+ Returns:
+ Boolean: True if the waypoint was prepared successfully
+ Raises:
+ ValidationError: If the waypoint cannot be prepared
+ """
+ self.ensure_one()
+ _logger.info(
+ _(
+ "Preparing waypoint %(waypoint)s on jet %(jet)s",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ )
+ )
+ if not self.state == "draft":
+ error = _(
+ "Cannot prepare waypoint %(waypoint)s on jet %(jet)s because"
+ " it is not in the 'draft' state",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ )
+ _logger.error(error)
+ raise ValidationError(error)
+
+ if self.waypoint_template_id.plan_create_id:
+ self.write({"state": "preparing", "is_destination": is_destination})
+ with self.env.cr.savepoint():
+ self.jet_id.server_id.sudo().run_flight_plan(
+ flight_plan=self.waypoint_template_id.plan_create_id,
+ jet=self.jet_id,
+ plan_log={
+ "waypoint_id": self.id,
+ },
+ variable_values=self._get_custom_variable_values(),
+ )
+ else:
+ self.write({"state": "ready", "is_destination": is_destination})
+ # Save jet variable values when state changes to ready
+ self._save_variable_values()
+
+ # Refresh the frontend views
+ self.env.user.reload_views(model="cx.tower.jet", rec_ids=[self.jet_id.id])
+
+ # Fly to this waypoint if set as destination
+ if is_destination:
+ self.fly_to()
+ else:
+ self._finalize_create_waypoint_command_log(success=True)
+ _logger.info(
+ _(
+ "Successfully prepared waypoint %(waypoint)s on jet %(jet)s",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ )
+ )
+ return True
+
+ def fly_to(self):
+ """
+ Fly to the waypoint
+
+ Returns:
+ bool: True if event was handled else False
+ """
+ self.ensure_one()
+ _logger.info(
+ _(
+ "Flying to waypoint %(waypoint)s on jet %(jet)s",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ )
+ )
+ if self.state != "ready":
+ error = _(
+ "Cannot fly to waypoint %(waypoint)s on jet %(jet)s because"
+ " it is not in the 'ready' state",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ )
+ _logger.error(error)
+ raise ValidationError(error)
+
+ # Cannot fly to waypoint if there is another waypoint
+ # in the "arriving" or state
+ other_waypoints = self.jet_id.waypoint_ids.filtered(
+ lambda w: w.state in ["arriving", "leaving"]
+ )
+ if other_waypoints:
+ error = _(
+ "Cannot fly to waypoint %(waypoint)s on jet %(jet)s because"
+ " there is another waypoint %(other_waypoint)s "
+ "in the 'arriving' or 'leaving' state",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ other_waypoint=other_waypoints[0].name,
+ )
+ _logger.error(error)
+ raise ValidationError(error)
+
+ # Leave the previous waypoint
+ previous_waypoint = self.jet_id.waypoint_id
+ if not previous_waypoint:
+ # No previous waypoint, set state to arriving
+ # Variable values will be restored in _arrive()
+ self.write({"state": "arriving", "is_destination": True})
+ self._arrive()
+ return True
+
+ # Don't go to the waypoint if it is already the current waypoint
+ if previous_waypoint.id == self.id:
+ return True
+
+ # Cannot leave the waypoint if it is not ready or current
+ if previous_waypoint.state not in ["ready", "current"]:
+ error = _(
+ "Cannot fly to waypoint %(waypoint)s on jet %(jet)s because"
+ " the previous waypoint %(previous_waypoint)s is not in the"
+ " 'ready' or 'current' state",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ previous_waypoint=previous_waypoint.name,
+ )
+ _logger.error(error)
+ raise ValidationError(error)
+
+ # Mark destination first; switch to arriving only after leave succeeds.
+ if not self.is_destination:
+ self.write({"is_destination": True})
+
+ # Leave the previous waypoint (this will save its variable values)
+ previous_waypoint._leave()
+ if previous_waypoint.state == "error":
+ # Roll back destination when source leave fails immediately.
+ self.write({"is_destination": False})
+ self._finalize_create_waypoint_command_log(
+ success=False,
+ error=_("Failed to leave current waypoint."),
+ )
+ return False
+ # If leaving completed immediately (no plan_leave_id),
+ # arrive at the new waypoint (which will restore variable values)
+ if self.state == "ready" and previous_waypoint.state in ["ready", "current"]:
+ self.write({"state": "arriving"})
+ self._arrive()
+ _logger.info(
+ _(
+ "Successfully flew to waypoint %(waypoint)s on jet %(jet)s",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ )
+ )
+ return True
+
+ def _leave(self):
+ """
+ Leave the waypoint.
+
+ Returns:
+ bool: True if event was handled else False
+ """
+ self.ensure_one()
+ if self.state not in ["ready", "current"]:
+ return False
+ self.write({"state": "leaving"})
+ plan_leave = self.waypoint_template_id.plan_leave_id
+ if plan_leave:
+ with self.env.cr.savepoint():
+ self.jet_id.server_id.sudo().run_flight_plan(
+ jet=self.jet_id,
+ flight_plan=plan_leave,
+ plan_log={
+ "waypoint_id": self.id,
+ },
+ variable_values=self._get_custom_variable_values(),
+ )
+ else:
+ self.write({"state": "ready"})
+ # Save jet variable values
+ self._save_variable_values()
+ return True
+
+ def _arrive(self):
+ """
+ Arrive at the waypoint.
+
+ Returns:
+ bool: True if event was handled else False
+ """
+ self.ensure_one()
+ if not self.state == "arriving":
+ return False
+ # Restore variable values before running the arrive plan
+ self._restore_variable_values()
+ plan_arrive = self.waypoint_template_id.plan_arrive_id
+ if plan_arrive:
+ self.jet_id.server_id.sudo().run_flight_plan(
+ jet=self.jet_id,
+ flight_plan=plan_arrive,
+ plan_log={
+ "waypoint_id": self.id,
+ },
+ variable_values=self._get_custom_variable_values(),
+ )
+ else:
+ # Clear destination flag when arriving without plan
+ self.write({"is_destination": False, "state": "current"})
+ self.jet_id.write({"waypoint_id": self.id})
+ self.jet_id.invalidate_recordset(["waypoint_id"])
+ self._finalize_create_waypoint_command_log(success=True)
+ # Refresh the frontend views
+ self.env.user.reload_views(model="cx.tower.jet", rec_ids=[self.jet_id.id])
+ return True
+
+ # ---------------------------
+ # --------- Hooks ---------
+ # ---------------------------
+ def _finalize_create_waypoint_command_log(self, success=True, error=None):
+ """Finish the command log that created this waypoint, if any.
+
+ Called when the waypoint reaches ready/current (success) or error.
+ Only calls finish() if the log is not already finished (guard against
+ double-finish). Does not clear created_from_command_log_id.
+
+ Args:
+ success (bool): True if waypoint reached ready/current.
+ error (str, optional): Error message when success is False.
+
+ Returns:
+ bool: True if command log was finished, False otherwise.
+ """
+ self.ensure_one()
+ log_record = self.created_from_command_log_id
+ if not log_record:
+ return False
+ if log_record.finish_date:
+ return False
+ status = 0 if success else (WAYPOINT_CREATE_FAILED if error else GENERAL_ERROR)
+ response = _("Waypoint reached %s", self.state) if success else None
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ return True
+
+ def _plan_finished(self, plan_log):
+ """
+ Handle the plan finished event
+
+ Args:
+ plan_log (cx.tower.plan.log): Plan log record
+
+ Returns:
+ bool: True if event was handled
+ """
+ self.ensure_one()
+ if plan_log.plan_status == 0:
+ # Successfully finished the plan
+ jet = self.jet_id # preserve in case of deleting
+
+ if self.state == "arriving":
+ # Set the waypoint as the current waypoint
+ # when successfully arriving
+ self.jet_id.write({"waypoint_id": self.id})
+ self.jet_id.invalidate_recordset(["waypoint_id"])
+ # Clear destination flag when successfully arrived
+ self.write({"state": "current", "is_destination": False})
+ self._finalize_create_waypoint_command_log(success=True)
+ _logger.info(
+ _(
+ "Successfully arrived at waypoint %(waypoint)s on jet %(jet)s",
+ waypoint=self.name,
+ jet=self.jet_id.name,
+ )
+ )
+ elif self.state == "deleting":
+ self.write({"state": "deleted"})
+ waypoint_name = self.name
+ jet_name = self.jet_id.name
+ self.unlink()
+ _logger.info(
+ _(
+ "Successfully deleted waypoint %(waypoint)s on jet %(jet)s",
+ waypoint=waypoint_name,
+ jet=jet_name,
+ )
+ )
+ elif self.state in ["leaving", "preparing"]:
+ # Save jet variable values
+ self._save_variable_values()
+
+ # Arrive at the destination waypoint
+ # if there is any in the arriving state (only for leaving)
+ if self.state == "leaving":
+ destination_waypoint = self.jet_id.waypoint_ids.filtered(
+ "is_destination"
+ )
+ if destination_waypoint:
+ destination_waypoint.write({"state": "arriving"})
+ destination_waypoint._arrive()
+
+ # Set the waypoint state to ready after leaving or preparing
+ prepared = self.state == "preparing"
+ self.write({"state": "ready"})
+ # Fly to this waypoint if set as destination
+ if self.is_destination and prepared:
+ self.fly_to()
+ else:
+ self._finalize_create_waypoint_command_log(success=True)
+
+ # Refresh the frontend views
+ self.env.user.reload_views(model="cx.tower.jet", rec_ids=[jet.id])
+ return True
+
+ # Failed to finish the plan
+ # - restore variable values from current waypoint
+ # - set the waypoint state to error
+ if self.state == "arriving":
+ # Restore variable values from jet's current waypoint
+ current_waypoint = self.jet_id.waypoint_id
+ if current_waypoint:
+ current_waypoint._restore_variable_values()
+ # Set current waypoint state to "current"
+ current_waypoint.write({"state": "current"})
+ # Clear destination flag when arriving fails
+ self.write({"is_destination": False, "state": "error"})
+ self._finalize_create_waypoint_command_log(
+ success=False, error=_("Plan failed while arriving.")
+ )
+ else:
+ if self.state == "leaving":
+ # Cancel pending destination when leave plan fails.
+ destination_waypoint = self.jet_id.waypoint_ids.filtered(
+ lambda w: w.is_destination and w.id != self.id
+ )
+ if destination_waypoint:
+ destination_waypoint.write({"is_destination": False})
+ destination_waypoint._finalize_create_waypoint_command_log(
+ success=False,
+ error=_("Failed to leave current waypoint."),
+ )
+ self.write({"state": "error", "is_destination": False})
+ self._finalize_create_waypoint_command_log(
+ success=False, error=_("Plan failed.")
+ )
+
+ # Refresh the frontend views
+ self.env.user.reload_views(model="cx.tower.jet", rec_ids=[self.jet_id.id])
+ return True
+
+ # -----------------------------------
+ # --------- Helper Methods ---------
+ # -----------------------------------
+ def _save_variable_values(self):
+ """
+ Save current jet variable values to the waypoint.
+ Only jet-specific values are saved (not template/server/global values).
+
+ Returns:
+ bool: True if values were saved
+ """
+ self.ensure_one()
+
+ # Get all variable values that belong to this jet specifically
+ # (not template/server/global values)
+ # Use variable_value_ids field from variable mixin
+ jet_variable_values = self.jet_id.variable_value_ids
+
+ # Build dictionary mapping variable_reference to value_char
+ variable_values_dict = {}
+ for var_value in jet_variable_values:
+ variable_values_dict[var_value.variable_reference] = (
+ var_value.value_char or ""
+ )
+
+ # Save to waypoint's variable_values field
+ self.write({"variable_values": variable_values_dict})
+ self.invalidate_recordset(["variable_values"])
+ return True
+
+ def _restore_variable_values(self):
+ """
+ Restore variable values from the waypoint to the jet.
+ - Removes all variable values that are not saved in the waypoint
+
+ Returns:
+ bool: True if values were restored
+ """
+ self.ensure_one()
+ if not self.variable_values:
+ # Remove all jet variable values if waypoint has no saved values
+ self.jet_id.variable_value_ids.unlink()
+ return True
+
+ # Get all current jet variable values
+ current_jet_values = self.jet_id.variable_value_ids
+ saved_references = set(self.variable_values.keys())
+
+ # Remove variable values that are not in the saved waypoint values
+ values_to_remove = current_jet_values.filtered(
+ lambda v: v.variable_reference not in saved_references
+ )
+ if values_to_remove:
+ values_to_remove.unlink()
+
+ # Restore each variable value from the saved dictionary
+ # Variable mixin handles checking if value is the same
+ for variable_reference, saved_value in self.variable_values.items():
+ self.jet_id.set_variable_value(variable_reference, saved_value)
+
+ return True
+
+ def _get_custom_variable_values(self):
+ """
+ Prepare custom variable values to pass with flight plans.
+ Following custom values are available:
+
+ __waypoint: waypoint reference
+ __waypoint_type: waypoint template reference
+ __waypoint_state: waypoint state
+ __waypoint_: waypoint metadata
+
+ Returns:
+ dict: Custom variable values to pass with flight plans
+ """
+ self.ensure_one()
+ custom_values = {
+ "__waypoint": self.reference,
+ "__waypoint_type": self.waypoint_template_id.reference,
+ "__waypoint_state": self.state,
+ }
+ if self.metadata:
+ for key, value in self.metadata.items():
+ custom_values[f"__waypoint_{key}"] = value
+ return custom_values
diff --git a/addons/cetmix_tower_server/models/cx_tower_jet_waypoint_template.py b/addons/cetmix_tower_server/models/cx_tower_jet_waypoint_template.py
new file mode 100644
index 0000000..ab2207d
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_jet_waypoint_template.py
@@ -0,0 +1,70 @@
+# Copyright (C) 2024 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models
+
+
+class CxTowerJetWaypointTemplate(models.Model):
+ """Jet Waypoint Templates define waypoints for jet templates"""
+
+ _name = "cx.tower.jet.waypoint.template"
+ _description = "Cetmix Tower Jet Waypoint Template"
+ _inherit = ["cx.tower.reference.mixin", "cx.tower.access.mixin"]
+ _order = "sequence, name asc"
+
+ name = fields.Char(required=True)
+ sequence = fields.Integer(default=10, help="Used to sort waypoints in views")
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ ondelete="cascade",
+ help="Jet template this waypoint template belongs to",
+ )
+ plan_create_id = fields.Many2one(
+ string="Create Flight Plan",
+ comodel_name="cx.tower.plan",
+ help="Flight plan to run after waypoint is created",
+ )
+ plan_arrive_id = fields.Many2one(
+ string="Arrive Flight Plan",
+ comodel_name="cx.tower.plan",
+ help="Flight plan to run after waypoint is reached",
+ )
+ plan_leave_id = fields.Many2one(
+ string="Leave Flight Plan",
+ comodel_name="cx.tower.plan",
+ help="Flight plan to run before leaving the waypoint",
+ )
+ plan_delete_id = fields.Many2one(
+ string="Delete Flight Plan",
+ comodel_name="cx.tower.plan",
+ help="Flight plan to run before deleting the waypoint",
+ )
+ note = fields.Text()
+
+ def _selection_access_level(self):
+ """
+ Available access levels
+
+ Returns:
+ List of tuples: available options.
+ """
+ return [
+ ("2", "Manager"),
+ ("3", "Root"),
+ ]
+
+ @api.depends("name", "jet_template_id", "jet_template_id.name")
+ def _compute_display_name(self):
+ """Compute record display name.
+
+ The UI should show waypoint templates in the format:
+ `` ()``.
+ """
+ for record in self:
+ jet_template_name = record.jet_template_id.name or "" # type: ignore[attr-defined]
+ if jet_template_name:
+ record.display_name = ( # type: ignore[attr-defined]
+ f"{record.name} ({jet_template_name})"
+ )
+ else:
+ record.display_name = record.name # type: ignore[attr-defined]
diff --git a/addons/cetmix_tower_server/models/cx_tower_key.py b/addons/cetmix_tower_server/models/cx_tower_key.py
new file mode 100644
index 0000000..3e275b7
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_key.py
@@ -0,0 +1,413 @@
+# 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 (Char): Key type selection value ('s' for secret, 'k' for SSH key)
+
+
+ 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
new file mode 100644
index 0000000..a50ea88
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_key_mixin.py
@@ -0,0 +1,70 @@
+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 = False
+
+ @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:
+ recordset: cx.tower.key recordset of secrets found in the 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
new file mode 100644
index 0000000..faa4115
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_key_value.py
@@ -0,0 +1,112 @@
+# 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_metadata_mixin.py b/addons/cetmix_tower_server/models/cx_tower_metadata_mixin.py
new file mode 100644
index 0000000..0d0b2f7
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_metadata_mixin.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2026 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+from odoo import api, fields, models
+
+
+class CxTowerMetadataMixin(models.AbstractModel):
+ """Used to implement metadata in models."""
+
+ _name = "cx.tower.metadata.mixin"
+ _description = "Cetmix Tower metadata mixin"
+
+ metadata = fields.Json(
+ help="Additional metadata for this record",
+ readonly=True,
+ groups="cetmix_tower_server.group_manager",
+ )
+ metadata_text = fields.Text(
+ help="Additional metadata for this record",
+ compute="_compute_metadata_text",
+ groups="cetmix_tower_server.group_manager",
+ )
+
+ @api.depends("metadata")
+ def _compute_metadata_text(self):
+ """
+ Compute the metadata text for the record
+ """
+ for record in self:
+ record.metadata_text = str(record.metadata) if record.metadata else False
+
+ def update_metadata(self, metadata):
+ """
+ Updates the metadata for the record.
+ Preserves the existing metadata.
+
+ Args:
+ metadata (dict): The metadata to update the record with
+
+ Returns:
+ bool: True if the metadata was updated, False otherwise
+ """
+ self.ensure_one()
+ # Preserve the existing data in self.metadata.
+ self.write({"metadata": {**(self.metadata or {}), **metadata}})
+ return True
diff --git a/addons/cetmix_tower_server/models/cx_tower_os.py b/addons/cetmix_tower_server/models/cx_tower_os.py
new file mode 100644
index 0000000..f98dc4c
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_os.py
@@ -0,0 +1,17 @@
+# 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
new file mode 100644
index 0000000..9a93b5f
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_plan.py
@@ -0,0 +1,423 @@
+# 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.exceptions import ValidationError
+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 = plan.line_ids.command_id
+
+ 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, jet_template=None, jet=None, **kwargs):
+ """Run single Flight Plan on a single server
+
+ Args:
+ server (cx.tower.server()): Server object
+ jet_template (cx.tower.jet.template()): jet template record
+ jet (cx.tower.jet()): jet 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.
+
+ Returns:
+ log_record (cx.tower.plan.log()): plan log record
+ """
+
+ self.ensure_one()
+ # Ensure we have a single server record
+ server.ensure_one()
+
+ # Check if Jet belongs to the server
+ if jet and jet.server_id != server:
+ raise ValidationError(
+ _(
+ "Jet %(jet)s does not belong to server %(server)s",
+ jet=jet.name,
+ server=server.name,
+ )
+ )
+
+ # Check plan access before running
+ # This is needed to avoid possible access violations
+ self.check_access("read")
+
+ # Save jet template and jet in kwargs
+ plan_log_vals = kwargs.get("plan_log", {})
+ if jet_template:
+ plan_log_vals["jet_template_id"] = jet_template.id
+ if jet:
+ plan_log_vals["jet_id"] = jet.id
+ kwargs["plan_log"] = plan_log_vals
+
+ # 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"
+ ):
+ domain = [
+ ("server_id", "=", server.id),
+ ("plan_id", "=", self.id), # type: ignore
+ ("is_running", "=", True),
+ ]
+ if jet_template:
+ domain.append(("jet_template_id", "=", jet_template.id))
+ if jet:
+ domain.append(("jet_id", "=", jet.id))
+ running_count = plan_log_obj.search_count(domain=domain)
+ 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=server,
+ plan=self,
+ **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
+ jet_template = command_log.jet_template_id
+ jet = command_log.jet_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=server,
+ jet_template=jet_template,
+ jet=jet,
+ 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 is not None:
+ 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.
+
+ Args:
+ action (Selection): Action to proceed
+ exit_code (Integer): Exit code
+ current_line (cx.tower.plan.line()): Current line
+
+ Returns:
+ action, exit_code, next_line (Selection, Integer, cx.tower.plan.line())
+ """
+ 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]
+
+ # Exit with command code if not exiting with custom code
+ if is_last_line and action != "ec":
+ 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=server,
+ jet_template=plan_log.jet_template_id,
+ jet=plan_log.jet_id,
+ 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
new file mode 100644
index 0000000..29798dd
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_plan_line.py
@@ -0,0 +1,315 @@
+# 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
+ if plan_log_record.waypoint_id:
+ kwargs["waypoint"] = plan_log_record.waypoint_id
+ server.run_command(
+ command=command_as_root,
+ path=path,
+ sudo=use_sudo,
+ jet_template=plan_log_record.jet_template_id,
+ jet=plan_log_record.jet_id,
+ **kwargs,
+ )
+
+ def _is_executable_line(
+ self, server, jet_template=None, jet=None, 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.
+ jet_template (cx.tower.jet.template()): The jet template being used.
+ jet (cx.tower.jet()): The jet being used.
+ 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:
+ variables = self.command_id.get_variables_from_code(condition) # pylint: disable=no-member
+ if variables:
+ variable_obj = self.env["cx.tower.variable"]
+ server_values = variable_obj._get_variable_values_by_references(
+ variables,
+ server=server,
+ jet_template=jet_template,
+ jet=jet,
+ )
+ # Merge with custom values passed to the flight plan (if any)
+ merged_values = {**server_values, **(variable_values or {})}
+ 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", {})
+ log_vals.update(
+ {
+ "plan_log_id": plan_log_record.id,
+ "condition": self.condition,
+ "is_skipped": True,
+ }
+ )
+
+ self.env["cx.tower.command.log"].record(
+ server_id=server.id,
+ command_id=self.command_id.id, # pylint: disable=no-member
+ start_date=now,
+ finish_date=now,
+ status=PLAN_LINE_CONDITION_CHECK_FAILED,
+ error=_("Plan line condition check failed."),
+ **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
new file mode 100644
index 0000000..04cdcc2
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_plan_line_action.py
@@ -0,0 +1,101 @@
+# 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
new file mode 100644
index 0000000..971c229
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_plan_log.py
@@ -0,0 +1,531 @@
+# 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 .constants import PLAN_IS_EMPTY, PLAN_STOPPED
+
+_logger = logging.getLogger(__name__)
+
+
+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",
+ )
+ server_id = fields.Many2one(
+ comodel_name="cx.tower.server", required=True, index=True, ondelete="cascade"
+ )
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ readonly=True,
+ index=True,
+ ondelete="cascade",
+ )
+ jet_template_install_id = fields.Many2one(
+ string="Jet Template Install Job",
+ comodel_name="cx.tower.jet.template.install",
+ readonly=True,
+ ondelete="cascade",
+ index=True,
+ help="Jet Template Install/Uninstall record being run. ",
+ )
+ jet_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ readonly=True,
+ index=True,
+ ondelete="cascade",
+ )
+ jet_action_id = fields.Many2one(
+ comodel_name="cx.tower.jet.action",
+ readonly=True,
+ help="Used to track flight plans executed by jet actions",
+ )
+ waypoint_id = fields.Many2one(
+ comodel_name="cx.tower.jet.waypoint",
+ help="Waypoint this plan log belongs to",
+ )
+
+ 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", "plan_id.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, jet_template=None, jet=None, variable_values=None
+ ):
+ """
+ Generator to get each line and check if it's executable.
+ Args:
+ plan (cx.tower.plan()): Flight Plan.
+ server (cx.tower.server()): Server.
+ jet_template (cx.tower.jet.template()): Jet Template.
+ jet (cx.tower.jet()): Jet.
+ Returns:
+ tuple: (line, is_executable)
+ """
+ for line in plan.line_ids:
+ yield (
+ line,
+ line._is_executable_line(
+ server=server,
+ jet_template=jet_template,
+ jet=jet,
+ 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=plan,
+ server=server,
+ jet_template=plan_log.jet_template_id,
+ jet=plan_log.jet_id,
+ variable_values=variable_values,
+ ):
+ if is_executable:
+ line._run(server=server, plan_log_record=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 {}),
+ "jet_template_id": plan_log.jet_template_id.id
+ if plan_log.jet_template_id
+ else None,
+ "jet_id": plan_log.jet_id.id if plan_log.jet_id else None,
+ },
+ )
+ 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
+ """
+ self.ensure_one()
+
+ values = {
+ "is_running": False,
+ "plan_status": plan_status,
+ "finish_date": fields.Datetime.now(),
+ }
+
+ # Apply kwargs
+ if kwargs:
+ values.update(kwargs)
+ self.sudo().write(values)
+
+ # Call the plan finished hook
+ # Use try/except to ensure that the plan finished hook is called
+ try:
+ # Savepoint to ensure that values are stored
+ # if something goes wrong.
+ with self.env.cr.savepoint():
+ self._plan_finished()
+ except Exception as e:
+ _logger.warning(
+ "Post-finish hook for plan '%s' failed: %s", self.plan_id.name, e
+ )
+ # Continue with the rest of the logic
+
+ # Jet Template action: only if it's not a sub-plan
+ # NB: Jet Template is always set automatically even if
+ # it's not provided explicitly when the plan is run.
+ if not self.jet_template_id or self.parent_flight_plan_log_id:
+ return
+
+ # Waypoint action: only if it's not a sub-plan
+ if self.waypoint_id:
+ try:
+ with self.env.cr.savepoint():
+ self.waypoint_id._plan_finished(self)
+ except Exception as e:
+ _logger.warning(
+ "Post-finish hook for waypoint '%s' failed: %s",
+ self.waypoint_id.name,
+ e,
+ )
+
+ # Finish template install/uninstall
+ if self.jet_template_install_id:
+ try:
+ with self.env.cr.savepoint():
+ self.jet_template_install_id._flight_plan_finished(
+ plan_status=self.plan_status,
+ )
+ except Exception as e:
+ _logger.warning(
+ "Post-finish hook for template install/uninstall "
+ "'%s'"
+ " failed: %s",
+ self.jet_template_install_id.name,
+ e,
+ )
+
+ # Jet
+ if self.jet_id and self.jet_action_id:
+ try:
+ with self.env.cr.savepoint():
+ self.jet_id._flight_plan_finished(
+ plan_status=self.plan_status,
+ )
+ except Exception as e:
+ _logger.warning(
+ "Post-finish hook for jet '%s' failed: %s", self.jet_id.name, e
+ )
+
+ def record(self, server, plan, status, **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.
+ """
+
+ vals = {
+ "server_id": server.id,
+ "plan_id": plan.id,
+ "start_date": 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)
+
+ plan_log = self.sudo().create(vals)
+ plan_log.finish(plan_status=status)
+ 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 only if not a jet-related plan
+ if (
+ self.plan_status == 0
+ and notification_type_success
+ and not self.jet_template_id
+ ):
+ # 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
+ # They are shown for jet-related plans and template installation/uninstallation
+ # as well to simplify the debugging process.
+ 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
new file mode 100644
index 0000000..959477b
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_reference_mixin.py
@@ -0,0 +1,480 @@
+# 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,
+ 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.env.registry.clear_cache()
+ 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.env.registry.clear_cache()
+ return res
+
+ def unlink(self):
+ """
+ Overrides unlink to clear cache for this method
+ """
+ res = super().unlink()
+ self.env.registry.clear_cache()
+ 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
new file mode 100644
index 0000000..b6c6a4c
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_scheduled_task.py
@@ -0,0 +1,442 @@
+import logging
+from datetime import timedelta
+
+from dateutil.relativedelta import relativedelta
+
+from odoo import _, api, fields, models
+from odoo.exceptions import ValidationError
+
+_logger = logging.getLogger(__name__)
+
+
+class CxTowerScheduledTask(models.Model):
+ """
+ Scheduled Tasks.
+ Used to schedule commands and flight plans to run on servers and jets.
+ """
+
+ _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",
+ )
+ jet_ids = fields.Many2many(
+ "cx.tower.jet",
+ "cx_tower_scheduled_task_jet_rel",
+ "scheduled_task_id",
+ "jet_id",
+ string="Jets",
+ )
+ jet_template_ids = fields.Many2many(
+ string="Jet Templates",
+ comodel_name="cx.tower.jet.template",
+ relation="cx_tower_jet_template_scheduled_task_rel",
+ column1="scheduled_task_id",
+ column2="jet_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"),
+ ("dow", "Days of Week"),
+ ("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."
+ )
+ # Days of week
+ monday = fields.Boolean()
+ tuesday = fields.Boolean()
+ wednesday = fields.Boolean()
+ thursday = fields.Boolean()
+ friday = fields.Boolean()
+ saturday = fields.Boolean()
+ sunday = fields.Boolean()
+
+ 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.constrains(
+ "interval_type",
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday",
+ )
+ def _check_days_of_week(self):
+ """
+ Check if at least one day of week is selected
+ """
+ for task in self:
+ if task.interval_type == "dow" and not any(
+ [
+ task.monday,
+ task.tuesday,
+ task.wednesday,
+ task.thursday,
+ task.friday,
+ task.saturday,
+ task.sunday,
+ ]
+ ):
+ raise ValidationError(
+ _(
+ "At least one day of week must be selected for the task '%s'.",
+ task.display_name,
+ )
+ )
+
+ @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:
+ if task.interval_type == "dow":
+ task.warning_message = False
+ continue
+ 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("Scheduled task %s failed: %s", task.id, 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, task.next_call),
+ "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
+
+ task: cx.tower.scheduled.task
+ from_date: datetime
+ """
+ if task.interval_type == "dow":
+ return self._get_next_call_dow(task, from_date)
+
+ 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 _get_task_selected_days(self, task):
+ """
+ Get list of selected weekday numbers for a task
+
+ task: cx.tower.scheduled.task
+ Returns: list of weekday numbers (0=Monday, 6=Sunday)
+ """
+ selected_days = []
+ if task.monday:
+ selected_days.append(0)
+ if task.tuesday:
+ selected_days.append(1)
+ if task.wednesday:
+ selected_days.append(2)
+ if task.thursday:
+ selected_days.append(3)
+ if task.friday:
+ selected_days.append(4)
+ if task.saturday:
+ selected_days.append(5)
+ if task.sunday:
+ selected_days.append(6)
+ return selected_days
+
+ def _get_next_call_dow(self, task, from_date):
+ """
+ Calculate next_call datetime for days of week interval type
+
+ task: cx.tower.scheduled.task
+ from_date: datetime
+ """
+ # Days of week: find next selected day at the same time
+ # weekday() returns 0=Monday, 6=Sunday
+ selected_days = self._get_task_selected_days(task)
+ if not selected_days:
+ raise ValidationError(
+ _(
+ "At least one day of week must be selected for the task '%s'.",
+ task.display_name,
+ )
+ )
+ current_weekday = from_date.weekday()
+
+ # Find next selected day (starting from tomorrow to get next occurrence)
+ # Check days in current week first (after today)
+ next_day = None
+ for day in selected_days:
+ if day > current_weekday:
+ next_day = day
+ break
+
+ # If no day found in current week, take first day of next week
+ if next_day is None:
+ next_day = min(selected_days)
+ days_ahead = (7 - current_weekday) + next_day
+ else:
+ days_ahead = next_day - current_weekday
+
+ # Create new datetime with same time, on the next selected day
+ next_date = from_date + timedelta(days=days_ahead)
+ return next_date.replace(
+ hour=from_date.hour,
+ minute=from_date.minute,
+ second=from_date.second,
+ microsecond=from_date.microsecond,
+ )
+
+ 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,
+ }
+ # Run for servers
+ for server in self.server_ids:
+ server.run_command(self.command_id, **kwargs)
+ # Run for jets
+ for jet in self.jet_ids:
+ jet.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,
+ }
+ # Run for servers
+ for server in self.server_ids:
+ server.run_flight_plan(self.plan_id, **kwargs)
+ # Run for jets
+ for jet in self.jet_ids:
+ jet.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
new file mode 100644
index 0000000..47a0925
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_scheduled_task_cv.py
@@ -0,0 +1,18 @@
+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
new file mode 100644
index 0000000..41ac87e
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_server.py
@@ -0,0 +1,2472 @@
+# 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,
+ JET_NOT_FOUND,
+ JET_TEMPLATE_NOT_FOUND,
+ NO_COMMAND_RUNNER_FOUND,
+ PYTHON_COMMAND_ERROR,
+ SSH_CONNECTION_ERROR,
+ WAYPOINT_CREATE_FAILED,
+ WAYPOINT_TEMPLATE_NOT_FOUND,
+)
+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("Error obtaining SSH connection: %s", 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("Error disconnecting SSH connection: %s", 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",
+ "cx.tower.metadata.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_counters",
+ )
+
+ # ---- 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",
+ )
+
+ # ---- Jets
+ jet_template_ids = fields.Many2many(
+ comodel_name="cx.tower.jet.template",
+ relation="cx_tower_jet_template_server_rel",
+ column1="server_id",
+ column2="jet_template_id",
+ string="Installed Jet Templates",
+ readonly=True,
+ copy=False,
+ )
+ jet_template_count = fields.Integer(
+ compute="_compute_counters",
+ )
+ jet_ids = fields.One2many(
+ comodel_name="cx.tower.jet",
+ inverse_name="server_id",
+ string="Jets",
+ copy=False,
+ )
+ jet_count = fields.Integer(
+ compute="_compute_counters",
+ )
+
+ # ---- 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"),
+ ]
+
+ # ---- Computed fields
+ def _compute_counters(self):
+ """
+ Compute total jet template, jets and files installed on server
+ Note: as numbers depend on the records user has access to,
+ we don't store the values.
+ @depends is not needed because they are displayed in the views only
+ or computed when accessed explicitly.
+ """
+ for server in self:
+ server.update(
+ {
+ "jet_template_count": len(server.jet_template_ids),
+ "jet_count": len(server.jet_ids),
+ "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=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
+ """
+ self.ensure_one()
+ 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
+ """
+ self.ensure_one()
+ 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_new_jet(self):
+ """
+ Returns wizard action to launch a jet
+ """
+ self.ensure_one()
+ context = self.env.context.copy()
+ context.update(
+ {
+ "default_server_id": self.id,
+ }
+ )
+ return {
+ "type": "ir.actions.act_window",
+ "name": _("Launch New Jet"),
+ "res_model": "cx.tower.jet.create.wizard",
+ "view_mode": "form",
+ "target": "new",
+ "context": context,
+ }
+
+ def action_open_files(self):
+ """
+ Open files of the current server
+ """
+ self.ensure_one()
+ 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
+
+ def action_open_jets(self):
+ """
+ Open jets of the current server
+ """
+ self.ensure_one()
+ action = self.env["ir.actions.actions"]._for_xml_id(
+ "cetmix_tower_server.cx_tower_jet_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
+
+ def action_install_jet_template(self):
+ """Action to install the Jet Template on the selected servers."""
+ self.ensure_one()
+ # Open the wizard to install the template on the selected servers
+ return {
+ "type": "ir.actions.act_window",
+ "name": _("Install Jet Template"),
+ "res_model": "cx.tower.jet.template.install.wiz",
+ "view_mode": "form",
+ "target": "new",
+ "context": {
+ "default_server_ids": self.ids, # pylint: disable=no-member
+ },
+ }
+
+ def action_uninstall_jet_template(self):
+ """
+ Uninstall jet template from the current server
+ """
+ self.ensure_one()
+ jet_template_id = self.env.context.get("jet_template_id")
+ if jet_template_id and jet_template_id in self.jet_template_ids.ids:
+ jet_template = self.env["cx.tower.jet.template"].browse(jet_template_id)
+ if jet_template:
+ jet_template.uninstall_from_servers(self)
+
+ # ------------------------------
+ # ---- 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
+ return {
+ "status": SSH_CONNECTION_ERROR,
+ "response": _("Connection failed."),
+ "error": e,
+ }
+
+ # Initialize test_result to None - will be set by try_command or try_file
+ test_result = None
+
+ # 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") or ""
+ error = test_result.get("error") or ""
+
+ # 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") or ""
+
+ # 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:
+ # Ensure test_result is set before accessing it
+ if test_result is None:
+ # Fallback: create a default success result
+ test_result = {
+ "status": 0,
+ "response": _("Connection test passed."),
+ "error": "",
+ }
+ response = test_result.get("response") or ""
+ return self._get_notification_action(
+ _(
+ "Connection test passed! \n%(res)s",
+ res=response.rstrip(),
+ ),
+ notification_type="info",
+ title=_("Success"),
+ sticky=False,
+ )
+
+ # Ensure test_result is set before returning
+ if test_result is None:
+ # Fallback: create a default success result
+ test_result = {
+ "status": 0,
+ "response": _("Connection test passed."),
+ "error": "",
+ }
+
+ 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("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
+
+ # ------------------------------
+ # ---- Command execution
+ # ------------------------------
+
+ def run_command(
+ self,
+ command,
+ path=None,
+ sudo=None,
+ ssh_connection=None,
+ jet_template=None,
+ jet=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.
+ jet_template (cx.tower.jet.template()) Jet Template record
+ Pass to run for specific jet template
+ jet (cx.tower.jet()): Jet record
+ Pass to run for specific jet
+ kwargs (dict): extra arguments. Use to pass external values.
+ Following keys are supported by default:
+ - "waypoint", cx.tower.jet.waypoint(): waypoint record
+ when running for a waypoint (e.g. from waypoint plan)
+ - "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()
+
+ # Check if jet belongs to the server
+ if jet and not jet.server_id == self:
+ raise ValidationError(
+ _(
+ "Jet '%(jet)s' doesn't belong to the server '%(server)s'.",
+ jet=jet.name,
+ server=self.name, # pylint: disable=no-member
+ )
+ )
+
+ # Force set jet template from jet if jet is provided
+ if jet:
+ jet_template = jet.jet_template_id
+
+ # 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", {}),
+ "jet_template_id": jet_template.id if jet_template else None,
+ "jet_id": jet.id if jet else None,
+ }
+ )
+ # 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_domain = [
+ ("server_id", "=", self.id),
+ ("command_id", "=", command.id),
+ ("is_running", "=", True),
+ ]
+ if jet_template:
+ another_command_running_domain.append(
+ ("jet_template_id", "=", jet_template.id)
+ )
+ if jet:
+ another_command_running_domain.append(("jet_id", "=", jet.id))
+ another_command_running_block = (
+ not command.allow_parallel_run
+ and log_obj.sudo().search_count(domain=another_command_running_domain)
+ )
+ # Another command is running, return error
+ if another_command_running_block:
+ 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=command,
+ path=path,
+ jet_template=jet_template,
+ jet=jet,
+ custom_variable_values=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,
+ jet_template=None,
+ jet=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
+ jet (cx.tower.jet()): Jet to render command for
+ custom_variable_values (dict): Custom variable values to render command
+ Returns:
+ dict: rendered values
+ {
+ "rendered_code": rendered command code,
+ "rendered_path": rendered command path
+ }
+ """
+ self.ensure_one()
+
+ variable_references = []
+
+ # 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 variable_references:
+ variable_references.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 variable_references:
+ variable_references.append(ve)
+
+ # If there are variables to render, get variable values
+ if variable_references:
+ # Get the variable values
+ variable_values = (
+ self.env["cx.tower.variable"]
+ .sudo()
+ ._get_variable_values_by_references(
+ variable_references, server=self, jet_template=jet_template, jet=jet
+ )
+ )
+
+ # 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`
+ Returns:
+ Bool: True if access is granted, False otherwise
+ """
+ # Check access rights first
+ return self.has_access(operation)
+
+ def run_flight_plan(self, flight_plan, jet_template=None, jet=None, **kwargs):
+ """
+ Runs flight plan on the current server.
+
+ Args:
+ flight_plan (cx.tower.plan()): flight plan to run
+ jet_template (cx.tower.jet.template()): jet template
+ to run the flight plan on
+ jet (cx.tower.jet()): jet to run the flight plan on
+ 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()
+
+ # Check if jet belongs to the server
+ if jet and not jet.server_id == self:
+ raise ValidationError(
+ _(
+ "Jet '%(jet)s' doesn't belong to the server '%(server)s'.",
+ jet=jet.name,
+ server=self.name, # pylint: disable=no-member
+ )
+ )
+
+ # Set jet template from jet if jet is provided
+ if jet:
+ jet_template = jet.jet_template_id
+
+ # Run flight plan
+ return flight_plan._run_single(
+ self, jet_template=jet_template, jet=jet, **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 == "jet_action":
+ response = self._command_runner_jet_action(
+ log_record,
+ **kwargs,
+ )
+ elif command.action == "create_waypoint":
+ response = self._command_runner_create_waypoint(
+ log_record,
+ **kwargs,
+ )
+ elif command.action == "plan":
+ response = self.with_context(
+ prevent_plan_recursion=True
+ )._command_runner_flight_plan(
+ log_record=log_record,
+ flight_plan=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, log_record, server_dir, **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:
+ log_record (recordset): Command log record.
+ server_dir (str): The directory on the server where the file should be
+ created.
+ **kwargs: Additional keyword arguments.
+
+ Returns:
+ record: The created file record.
+ """
+ file_template_id = log_record.command_id.file_template_id
+ return file_template_id.create_file(
+ server=self,
+ server_dir=server_dir,
+ if_file_exists=log_record.command_id.if_file_exists,
+ jet_template=log_record.jet_template_id,
+ jet=log_record.jet_id,
+ )
+
+ 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
+ file = self._command_runner_file_using_template_create_file(
+ log_record=log_record,
+ server_dir=server_dir,
+ )
+
+ # 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.pop("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
+ plan_log_record = None
+ 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(
+ server=self,
+ jet_template=log_record.jet_template_id,
+ jet=log_record.jet_id,
+ **kwargs,
+ )
+ except Exception as e: # pylint: disable=broad-exception-caught
+ if raise_on_error:
+ raise ValidationError(
+ _("Flight plan running error %(err)s", err=e)
+ ) from e
+ 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
+ if plan_log_record
+ else None,
+ )
+ 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
+
+ def _command_runner_jet_action(
+ self,
+ log_record,
+ **kwargs,
+ ):
+ """
+ Run Jet action.
+ Updates the record in the Command Log (cx.tower.command.log)
+
+ Args:
+ log_record (cx.tower.command.log()): Command log record
+
+ Returns:
+ dict(): jet action running result if `log_record` is
+ not defined else None
+
+ Raises:
+ ValidationError: if `log_record` is not defined
+ """
+ if not log_record:
+ raise ValidationError(
+ _("Command log is required for 'Jet Action' commands!")
+ )
+
+ # Initialize result values
+ status = 0
+ response = None
+ error = None
+ dependent_jets = None
+
+ # Get the action from the command
+ action = log_record.command_id.jet_action_id
+ if not action:
+ status = GENERAL_ERROR
+ error = _("Jet action is not found.")
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ return {"status": status, "response": response, "error": error}
+
+ jet_for_which_command_is_run = log_record.jet_id
+ requested_jet_template = log_record.command_id.jet_template_id
+
+ if not jet_for_which_command_is_run:
+ status = JET_NOT_FOUND
+ error = _("Jet for which command is run is not found.")
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ return {"status": status, "response": response, "error": error}
+ if not requested_jet_template:
+ status = JET_TEMPLATE_NOT_FOUND
+ error = _("Jet template is not found.")
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ return {"status": status, "response": response, "error": error}
+
+ # Trigger for the jet itself if the same jet template is used
+ # This is used when you want to trigger an action for
+ # the same jet for which the command is run.
+ if jet_for_which_command_is_run.jet_template_id == requested_jet_template:
+ dependent_jets = jet_for_which_command_is_run
+ else:
+ # Get dependent jets by template
+ dependent_jets = (
+ jet_for_which_command_is_run._get_dependent_jets_by_template(
+ requested_jet_template
+ )
+ )
+
+ if dependent_jets:
+ # Trigger the action for all dependent jets; aggregate failures as
+ # "ref: message, ref2: message2" for the command log.
+ error_parts = []
+ for jet in dependent_jets:
+ result = jet._trigger_action(
+ action=action,
+ raise_if_not_available=False,
+ )
+ if not result:
+ continue
+ jet_status = result.get("status", 0)
+ jet_error = result.get("error")
+ if jet_status == 0 and not jet_error:
+ continue
+ if jet_error:
+ error_parts.append(f"{jet.reference}: {jet_error}")
+ else:
+ error_parts.append(
+ _(
+ "%(jet)s: action failed (status %(status)s)",
+ jet=jet.reference,
+ status=jet_status,
+ )
+ )
+ # Compose the main message
+ jet_references = ", ".join(jet.reference for jet in dependent_jets)
+
+ main_message = _(
+ "Action triggered for %(jet_references)s",
+ jet_references=jet_references,
+ )
+
+ if error_parts:
+ status = GENERAL_ERROR
+ error = "\n".join(
+ [
+ main_message,
+ (
+ error_parts[0]
+ if len(error_parts) == 1
+ else ", ".join(error_parts)
+ ),
+ ]
+ )
+ response = None
+ else:
+ response = main_message
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ # If no dependent jets, finish the command
+ else:
+ status = 0 # no dependent jets, so the command is finished with no error
+ response = _(
+ "Jet %(jet)s has no dependent jets with template %(template)s.",
+ jet=jet_for_which_command_is_run.name,
+ template=requested_jet_template.name,
+ )
+
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ # Return result
+ return {"status": status, "response": response, "error": error}
+
+ def _command_runner_create_waypoint(self, log_record, **kwargs):
+ """Run Create a Waypoint command.
+
+ Creates a waypoint for the plan's jet from the command's waypoint template.
+ The flight plan sets the jet as busy, so we pass ignore_busy=True so
+ creation is allowed. The command log is not finished here when a waypoint
+ is created; the waypoint callback (_finalize_create_waypoint_command_log)
+ finishes it when the waypoint reaches ready/current or error.
+
+ Args:
+ log_record (cx.tower.command.log): Command log record.
+
+ Returns:
+ dict: status, response (e.g. waypoint id when created), error.
+ When waypoint is created, status 0 and response indicate
+ deferred completion; the log stays running until the callback.
+ """
+ if not log_record:
+ raise ValidationError(
+ _("Command log is required for 'Create a Waypoint' commands!")
+ )
+ command = log_record.command_id
+ jet = log_record.jet_id
+ waypoint_template = command.waypoint_template_id
+
+ status = 0
+ response = None
+ error = None
+
+ if not jet:
+ status = JET_NOT_FOUND
+ error = _("Jet for which command is run is not found.")
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ return {"status": status, "response": response, "error": error}
+
+ if not waypoint_template:
+ status = WAYPOINT_TEMPLATE_NOT_FOUND
+ error = _("Waypoint template is not set.")
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ return {"status": status, "response": response, "error": error}
+
+ try:
+ waypoint = jet.create_waypoint(
+ waypoint_template,
+ fly_here=command.fly_here,
+ ignore_busy=True,
+ created_from_command_log=log_record,
+ )
+ except ValidationError:
+ waypoint = False
+
+ if not waypoint:
+ status = WAYPOINT_CREATE_FAILED
+ error = _(
+ "Waypoint creation failed (e.g. waypoint template "
+ "does not match jet template)."
+ )
+ log_record.finish(
+ status=status,
+ response=response,
+ error=error,
+ )
+ return {"status": status, "response": response, "error": error}
+
+ # Do not finish the log; waypoint callback will finish it when
+ # ready/current/error
+ response = {"waypoint_id": waypoint.id}
+ return {"status": 0, "response": response, "error": None}
+
+ @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("SSH run command error: %s", 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", {})
+ )
+
+ # Check if code contains banned keywords
+ banned_keywords = self.env[
+ "cx.tower.command"
+ ]._get_banned_python_code_keywords()
+ for banned_keyword in banned_keywords:
+ if banned_keyword in code:
+ raise ValidationError(
+ _(
+ "Following keyword is not allowed in Python code:"
+ " '%(banned_keyword)s'",
+ banned_keyword=banned_keyword,
+ )
+ )
+ # Get jet template, jet and waypoint from kwargs or log
+ log_vals = kwargs.get("log", {})
+ if log_vals:
+ jet_template_id = log_vals.get("jet_template_id")
+ jet_id = log_vals.get("jet_id")
+ else:
+ jet_template_id = kwargs.get("jet_template_id")
+ jet_id = kwargs.get("jet_id")
+ waypoint = kwargs.get("waypoint")
+
+ jet_template = (
+ self.env["cx.tower.jet.template"].browse(jet_template_id)
+ if jet_template_id
+ else None
+ )
+ jet = self.env["cx.tower.jet"].browse(jet_id) if jet_id else None
+
+ # Get the evaluation context for the python command
+ eval_context = self.env[
+ "cx.tower.command"
+ ]._get_python_command_eval_context(
+ server=self,
+ jet_template=jet_template,
+ jet=jet,
+ waypoint=waypoint,
+ 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: # pylint: disable=broad-exception-caught
+ if raise_on_error:
+ raise ValidationError(
+ _("Python code running error: %(err)s", err=e)
+ ) from e
+ 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:
+ 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 get_variable_value(self, variable_reference, no_fallback=False):
+ """
+ Return the value of a variable for the current server.
+ NB: this function follows the value application order.
+ So it will return the global value if server value is not set.
+
+ Returns:
+ str: The value of the variable for the current record or None
+ """
+ self.ensure_one()
+ variable = self.env["cx.tower.variable"].get_by_reference(variable_reference)
+ if not variable:
+ return None
+ values = variable._get_variable_values_by_references(
+ variable_references=[variable_reference], server=self
+ )
+ return values[variable_reference]
+
+ def server_toggle_active(self, self_active):
+ """
+ Change active status of related records:
+ - files
+ - commands
+ - plans
+ - variable values
+ Add custom logic to your model if you want to change
+ the active status of other 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):
+ """Archive or unarchive 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
new file mode 100644
index 0000000..14512da
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_server_log.py
@@ -0,0 +1,238 @@
+# 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",
+ compute="_compute_server_id",
+ index=True,
+ store=True,
+ readonly=False,
+ copy=False,
+ )
+ 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",
+ copy=False,
+ )
+ 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",
+ )
+
+ # -- Jet Template related
+ jet_template_id = fields.Many2one(
+ "cx.tower.jet.template",
+ ondelete="cascade",
+ index=True,
+ help="This jet template will be used to create log files when jet is created",
+ )
+
+ # -- Jet related
+ jet_id = fields.Many2one(
+ "cx.tower.jet",
+ ondelete="cascade",
+ index=True,
+ )
+
+ @api.depends("jet_id")
+ def _compute_server_id(self):
+ for record in self:
+ if not record.server_id and record.jet_id:
+ record.server_id = record.jet_id.server_id.id
+
+ 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
+ """
+ # Remove the null bytes
+ return log_text.replace("\x00", "")
+
+ 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":
+ self.file_id.download(raise_error=False)
+ return self.file_id.code
+ if self.file_id.source == "tower":
+ result = self.file_id.action_get_current_server_code()
+ if isinstance(result, dict):
+ return
+ 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,
+ jet=self.jet_id,
+ jet_template=self.jet_template_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
+ self.ensure_one()
+ 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
new file mode 100644
index 0000000..6806698
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_server_template.py
@@ -0,0 +1,653 @@
+# 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)
+ MAGIC_FIELDS = models.MAGIC_COLUMNS
+
+ # 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
new file mode 100644
index 0000000..7418d7e
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_shortcut.py
@@ -0,0 +1,97 @@
+# 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 shortcut is triggered.
+ Override to implement custom notifications.
+
+ Args:
+ server (cx.tower.server()): Server action was triggered for
+
+ Returns:
+ Boolean: True if notification was sent.
+ """
+ self.ensure_one()
+
+ self.env.user.notify_info(
+ title=server.name,
+ message=_(
+ "Shortcut '%(shr)s' triggered. Check %(t)s log for result",
+ shr=self.name,
+ t="flight plan" if self.action == "plan" else "command",
+ ),
+ sticky=False,
+ )
+ return True
diff --git a/addons/cetmix_tower_server/models/cx_tower_tag.py b/addons/cetmix_tower_server/models/cx_tower_tag.py
new file mode 100644
index 0000000..f7fd206
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_tag.py
@@ -0,0 +1,91 @@
+# 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
new file mode 100644
index 0000000..b36a929
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_tag_mixin.py
@@ -0,0 +1,116 @@
+# 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
new file mode 100644
index 0000000..05b21ab
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_template_mixin.py
@@ -0,0 +1,217 @@
+# 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.
+ """
+
+ # Return the original code if it's empty.
+ # So if it's False then we preserve the original 'False' value.
+ if not code:
+ return code
+
+ 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',..])
+ """
+ if not code:
+ return []
+ 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
new file mode 100644
index 0000000..62544bc
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_variable.py
@@ -0,0 +1,900 @@
+# Copyright (C) 2022 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+import logging
+import uuid
+from urllib.parse import urlparse
+
+from odoo import api, fields, models
+from odoo.tools import LazyTranslate
+from odoo.tools.safe_eval import safe_eval, wrap_module
+
+_lt = LazyTranslate(__name__, default_lang="en_US")
+
+
+_logger = logging.getLogger(__name__)
+
+re = wrap_module(
+ __import__("re"),
+ [
+ "match",
+ "fullmatch",
+ "search",
+ "sub",
+ "subn",
+ "split",
+ "findall",
+ "finditer",
+ "compile",
+ "template",
+ "escape",
+ "error",
+ ],
+)
+
+# Maximum recursion depth for variable value rendering
+# to prevent infinite loops
+MAX_DEPTH = 10
+
+
+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 = _lt("Invalid value!")
+ SYSTEM_VARIABLE_REFERENCE = "tower"
+
+ 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):
+ """Open the variable values"""
+ self.ensure_one()
+ context = self.env.context.copy()
+ context.update(
+ {
+ "default_variable_id": self.id,
+ }
+ )
+
+ return {
+ "type": "ir.actions.act_window",
+ "name": self.env._("Variable Values"),
+ "res_model": "cx.tower.variable.value",
+ "views": [[False, "list"]],
+ "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": self.env._("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": self.env._("Variable Values"),
+ "res_model": "cx.tower.variable.value",
+ "views": [[False, "list"]],
+ "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) # pylint: disable=no-member
+ ):
+ return True, None
+ message = self.validation_message or self.DEFAULT_VALIDATION_MESSAGE
+ return (
+ False,
+ self.env._(
+ "Variable: %(var)s, Value: %(val)s\n%(msg)s",
+ msg=message,
+ var=self.name, # pylint: disable=no-member
+ val=value_char,
+ ),
+ )
+
+ # ------------------------------
+ # ---- Managing variable values
+ # ------------------------------
+ def _get_value(
+ self,
+ server=None,
+ server_template=None,
+ plan_line_action=None,
+ jet_template=None,
+ jet=None,
+ ):
+ """Get the value of the variable.
+
+ 0. No arguments: return the global value.
+ 1. Server Template: return the Server Template specific value
+ or the global value.
+ 2. Server: return the Server specific value or the global value.
+ 3. Jet Template: return the Jet Template specific value
+ or the Server value
+ or the global value.
+ 4. Jet: return the Jet specific value
+ or the Jet Template value
+ or the Server value
+ or the global value.
+ 5. Plan Line Action: return the Plan Line Action specific value.
+
+ Args:
+ server (cx.tower.server): Server
+ server_template (cx.tower.server.template): Server Template
+ plan_line_action (cx.tower.plan.line.action): Plan Line Action
+ jet_template (cx.tower.jet.template): Jet Template
+ jet (cx.tower.jet): Jet
+
+ Returns:
+ Char: The value of the variable or None if no value is found.
+ """
+ self.ensure_one()
+ values = self.value_ids
+
+ # 0. Set server and jet template from jet
+ # if jet is provided
+ if jet:
+ server = jet.server_id
+ jet_template = jet.jet_template_id
+
+ # 1. Prepare the values
+
+ # Initialize all values to None
+ global_value_char = server_value_char = server_template_value_char = (
+ plan_line_action_value_char
+ ) = jet_template_value_char = jet_value_char = None
+
+ # Get origin id's in case we are dealing with onchange()
+ server_id = (
+ server._origin.id
+ if server and hasattr(server, "_origin")
+ else server.id
+ if server
+ else None
+ )
+ server_template_id = (
+ server_template._origin.id
+ if server_template and hasattr(server_template, "_origin")
+ else server_template.id
+ if server_template
+ else None
+ )
+ plan_line_action_id = (
+ plan_line_action._origin.id
+ if plan_line_action and hasattr(plan_line_action, "_origin")
+ else plan_line_action.id
+ if plan_line_action
+ else None
+ )
+ jet_template_id = (
+ jet_template._origin.id
+ if jet_template and hasattr(jet_template, "_origin")
+ else jet_template.id
+ if jet_template
+ else None
+ )
+ jet_id = (
+ jet._origin.id
+ if jet and hasattr(jet, "_origin")
+ else jet.id
+ if jet
+ else None
+ )
+
+ # Check all values for the variable and assign them.
+ # Note: we are not using filtered() to avoid multiple iterations
+ # on the same recordset.
+ for variable_value in values:
+ # Fetch the server value
+ if (
+ server
+ and server_value_char is None
+ and variable_value.server_id.id == server_id
+ ):
+ server_value_char = variable_value.value_char
+ continue
+ # Fetch the server template value
+ if (
+ server_template
+ and server_template_value_char is None
+ and variable_value.server_template_id.id == server_template_id
+ ):
+ server_template_value_char = variable_value.value_char
+ continue
+ # Fetch the plan line action value
+ if (
+ plan_line_action
+ and plan_line_action_value_char is None
+ and variable_value.plan_line_action_id.id == plan_line_action_id
+ ):
+ plan_line_action_value_char = variable_value.value_char
+ continue
+ # Fetch the jet template value
+ if (
+ jet_template
+ and jet_template_value_char is None
+ and variable_value.jet_template_id.id == jet_template_id
+ ):
+ jet_template_value_char = variable_value.value_char
+ continue
+ # Fetch the jet value
+ if jet and jet_value_char is None and variable_value.jet_id.id == jet_id:
+ jet_value_char = variable_value.value_char
+ continue
+ # Fetch the global value
+ if global_value_char is None and variable_value.is_global:
+ global_value_char = variable_value.value_char
+
+ # 2. Compose the response
+ # 2.1. Server Template
+ if server_template:
+ return server_template_value_char or global_value_char
+
+ # 2.2. Jet
+ if jet:
+ return (
+ jet_value_char
+ if jet_value_char is not None
+ else jet_template_value_char
+ if jet_template_value_char is not None
+ else server_value_char
+ if server_value_char is not None
+ else global_value_char
+ )
+
+ # 2.3. Jet Template
+ if jet_template:
+ return (
+ jet_template_value_char
+ if jet_template_value_char is not None
+ else server_value_char
+ if server_value_char is not None
+ else global_value_char
+ )
+
+ # 2.4. Server
+ if server:
+ return (
+ server_value_char
+ if server_value_char is not None
+ else global_value_char
+ )
+
+ # 2.5. Plan Line Action
+ if plan_line_action:
+ return plan_line_action_value_char
+
+ # 2.6. Global
+ return global_value_char
+
+ @api.model
+ def _get_variable_values_by_references(
+ self,
+ variable_references,
+ apply_modifiers=True,
+ **kwargs,
+ ):
+ """Get variable values for multiple references.
+ This method is designed to be used for template rendering.
+ It also includes system variable values in the result.
+
+ Args:
+ variable_references (list of Char): variable names
+ apply_modifiers (bool): apply Python modifiers to the values
+ **kwargs: keyword arguments to pass to the _get_value method
+ - server (cx.tower.server): Server
+ - server_template (cx.tower.server.template): Server Template
+ - plan_line_action (cx.tower.plan.line.action): Plan Line Action
+ - jet_template (cx.tower.jet.template): Jet Template
+ - jet (cx.tower.jet): Jet
+ - _depth (int): Depth of the recursion
+ Returns:
+ dict {variable_reference: value}
+ """
+ # 0. Get keyword arguments
+ server = kwargs.get("server")
+ server_template = kwargs.get("server_template")
+ plan_line_action = kwargs.get("plan_line_action")
+ jet_template = kwargs.get("jet_template")
+ jet = kwargs.get("jet")
+ _depth = kwargs.get("_depth", 0)
+
+ # 0. Update server and jet template from jet
+ if jet:
+ server = jet.server_id
+ jet_template = jet.jet_template_id
+
+ # 1. Get system variable values
+ variable_values = {}
+ system_vars = self._get_system_variable_values(
+ server=server, jet_template=jet_template, jet=jet
+ )
+ if system_vars:
+ variable_values[self.SYSTEM_VARIABLE_REFERENCE] = system_vars
+
+ # Return just system variable values if no references are provided
+ # or the only one is the system variable
+ # Need a fallback in case system variable is provides several times
+ if not variable_references or (
+ all(
+ reference == self.SYSTEM_VARIABLE_REFERENCE
+ for reference in variable_references
+ )
+ ):
+ return variable_values
+
+ # 2. Get variable value records
+ for reference in variable_references:
+ # Do not overwrite system variable values
+ if reference == self.SYSTEM_VARIABLE_REFERENCE:
+ continue
+ variable = self.get_by_reference(reference) # pylint: disable=no-member
+
+ # Assign the value to the variable values dictionary
+ variable_value = (
+ variable._get_value(
+ server=server,
+ server_template=server_template,
+ plan_line_action=plan_line_action,
+ jet_template=jet_template,
+ jet=jet,
+ )
+ if variable
+ else None
+ )
+ variable_values[reference] = variable_value
+
+ # 3. Render templates in values
+ self._render_variable_values(
+ variable_values,
+ server=server,
+ jet_template=jet_template,
+ jet=jet,
+ _depth=_depth,
+ )
+
+ # 4. Apply modifiers
+ if apply_modifiers:
+ self._apply_modifiers(variable_values)
+
+ return variable_values
+
+ def _render_variable_values(self, variable_values, **kwargs):
+ """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): variable values to complete
+ **kwargs: keyword arguments to pass to the _get_value method
+ - server (cx.tower.server): Server
+ - server_template (cx.tower.server.template): Server Template
+ - plan_line_action (cx.tower.plan.line.action): Plan Line Action
+ - jet_template (cx.tower.jet.template): Jet Template
+ - jet (cx.tower.jet): Jet
+ - _depth (int): Depth of the recursion
+ """
+ # 0. Get keyword arguments
+ server = kwargs.get("server")
+ jet_template = kwargs.get("jet_template")
+ jet = kwargs.get("jet")
+ _depth = kwargs.get("_depth", 0)
+
+ # Control recursion depth
+ _depth += 1
+ if _depth > MAX_DEPTH:
+ _logger.error("Max depth %d reached for variable %s", _depth, self.name)
+ return
+
+ TemplateMixin = self.env["cx.tower.template.mixin"]
+ for key, var_value in variable_values.items():
+ # Skip system variable values
+ if not var_value or key == self.SYSTEM_VARIABLE_REFERENCE:
+ continue
+
+ # Render only if template is found
+ if "{{" in 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
+ values_for_value = self._get_variable_values_by_references(
+ value_vars,
+ apply_modifiers=True,
+ server=server,
+ jet_template=jet_template,
+ jet=jet,
+ _depth=_depth,
+ )
+
+ # Render value using variables
+ variable_values[key] = TemplateMixin.render_code_custom(
+ var_value, **values_for_value
+ )
+
+ def _apply_modifiers(self, variable_values):
+ """Apply pre-defined Python expression to the dictionary
+ of variable values.
+
+ Args:
+ variable_values (dict): variable values
+ {variable_reference: value}
+ """
+
+ for variable_reference, value in variable_values.items():
+ if not value:
+ continue
+
+ # ORM should cache resolved variables
+ variable = self.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._get_eval_context(value)
+ try:
+ safe_eval(
+ variable.applied_expression,
+ eval_context,
+ mode="exec",
+ nocopy=True,
+ )
+ variable_values[variable_reference] = eval_context.get("result", value)
+ except Exception as e:
+ _logger.error(
+ "Error evaluating applied expression for "
+ "variable %s value %s: %s",
+ variable.name,
+ value,
+ str(e),
+ )
+
+ @api.model
+ def _get_system_variable_values(self, server=None, jet_template=None, jet=None):
+ """
+ Get the values for the `tower` system variable.
+ This variable uses `tower..` format.
+ E.g. `tower.server.ipv6`, `tower.tools.uuid`,
+ `tower.jet_template.reference`, `tower.tools.now_underscore` etc.
+
+
+ Args:
+ server (cx.tower.server()): server record
+ jet_template (cx.tower.jet.template()): jet template record
+ jet (cx.tower.jet()): jet record
+
+ Returns:
+ dict(): `tower` values.
+ {
+ 'tools': {..helper tools vals...}
+ 'server': {..server vals..},
+ 'jet_template': {..jet template vals..},
+ 'jet': {..jet vals..},
+ }
+ """
+ return {
+ "tools": self._parse_system_variable_tools(),
+ "server": self._parse_system_variable_server(server),
+ "jet_template": self._parse_system_variable_jet_template(jet_template),
+ "jet": self._parse_system_variable_jet(jet),
+ }
+
+ def _parse_system_variable_server(self, server=None):
+ """Parser system variable of `server` type.
+
+ Args:
+ server (cx.tower.server()): server record
+
+ Returns:
+ dict(): `server` values of the `tower` variable.
+ """
+ # Get current server
+ values = {}
+ if server:
+ # Using sudo() to get all fields
+ server = server.sudo()
+ 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,
+ }
+ if server.url:
+ url_parts = urlparse(server.url)
+ values.update(
+ {
+ "hostname": url_parts.hostname,
+ "netloc": url_parts.netloc,
+ "port": url_parts.port,
+ }
+ )
+ return values
+
+ def _parse_system_variable_jet_template(self, jet_template=None):
+ """Parser system variable of `server` type.
+
+ Args:
+ jet_template (cx.tower.jet.template()): jet template record
+
+ Returns:
+ dict(): `jet_template` values of the `tower` variable.
+ """
+ # Get current server
+ values = {}
+ if jet_template:
+ # Using sudo() to get all fields
+ jet_template = jet_template.sudo()
+ values = {
+ "name": jet_template.name,
+ "reference": jet_template.reference,
+ }
+ return values
+
+ def _parse_system_variable_jet(self, jet=None):
+ """Parser system variable of `jet` type.
+
+ Args:
+ jet (cx.tower.jet()): jet record
+ """
+ values = {}
+ if jet:
+ # Using sudo() to get all fields
+ jet = jet.sudo()
+ values = {
+ "name": jet.name,
+ "reference": jet.reference,
+ "url": jet.url,
+ "state": jet.state,
+ "cloned_from": jet.jet_cloned_from_id.reference
+ if jet.jet_cloned_from_id
+ else False,
+ }
+ # Add URL parts if URL is set
+ if jet.url:
+ url_parts = urlparse(jet.url)
+ else:
+ url_parts = False
+ values.update(
+ {
+ "hostname": url_parts.hostname
+ if url_parts and url_parts.hostname
+ else False,
+ "netloc": url_parts.netloc
+ if url_parts and url_parts.netloc
+ else False,
+ "port": url_parts.port if url_parts and url_parts.port else False,
+ }
+ )
+ # Add waypoint values if waypoint is set
+ waypoint_data = {
+ "reference": jet.waypoint_id.reference if jet.waypoint_id else False,
+ "type": jet.waypoint_id.waypoint_template_id.reference
+ if jet.waypoint_id
+ else False,
+ }
+ # Add each metadata key-value pair to the waypoint data
+ metadata = jet.waypoint_id.metadata if jet.waypoint_id else False
+ if metadata:
+ for key, value in metadata.items():
+ waypoint_data[key] = value
+ values.update({"waypoint": waypoint_data})
+ return values
+
+ def _parse_system_variable_tools(self):
+ """Parser system variable of `tools` type.
+
+ Returns:
+ dict(): `tools` 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
diff --git a/addons/cetmix_tower_server/models/cx_tower_variable_mixin.py b/addons/cetmix_tower_server/models/cx_tower_variable_mixin.py
new file mode 100644
index 0000000..382aae2
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_variable_mixin.py
@@ -0,0 +1,82 @@
+# 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 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_value(self, variable_reference, no_fallback=False):
+ """Get the value of a variable.
+ IMPORTANT: This is the generic method that returns the value of the variable
+ for the current record.
+ It doesn't evaluate fallback values,eg "jet->template->server->global".
+ Inherit and override this method to implement a proper value parsing logic.
+
+ Args:
+ variable_reference (str): The reference of the variable to get the value for
+ no_fallback (bool): If True, return current record value
+ without checking fallback values.
+ Returns:
+ str: The value of the variable for the current record or None
+ """
+ self.ensure_one()
+
+ # Get the variable value for the current record
+ variable_value = self.variable_value_ids.filtered(
+ lambda v: v.variable_reference == variable_reference
+ )
+ if variable_value:
+ return variable_value.value_char
+
+ def set_variable_value(self, variable_reference, value):
+ """Set the value of a variable.
+
+ Args:
+ variable_reference (str): The reference of the variable to set the value for
+ value (str): The value to set for the variable
+ """
+ self.ensure_one()
+
+ # Check if the variable value exists and update it
+ variable_value = self.variable_value_ids.filtered(
+ lambda v: v.variable_reference == variable_reference
+ )
+ if variable_value:
+ # Do nothing if the value is the same
+ if variable_value.value_char == value:
+ return
+ variable_value.value_char = value
+ return
+
+ # Get the variable
+ variable = self.env["cx.tower.variable"].get_by_reference(variable_reference)
+ if not variable:
+ raise ValidationError(
+ _(
+ "Variable '%(variable_reference)s' not found",
+ variable_reference=variable_reference,
+ )
+ )
+
+ # Create a new variable value
+ self.write(
+ {
+ "variable_value_ids": [
+ (0, 0, {"variable_id": variable.id, "value_char": value})
+ ]
+ }
+ )
diff --git a/addons/cetmix_tower_server/models/cx_tower_variable_option.py b/addons/cetmix_tower_server/models/cx_tower_variable_option.py
new file mode 100644
index 0000000..b5b98e0
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_variable_option.py
@@ -0,0 +1,117 @@
+# 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 SQL constraints to ensure uniqueness of
+ # 'value_char' and 'name' per variable
+ _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
new file mode 100644
index 0000000..06709a5
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_variable_value.py
@@ -0,0 +1,584 @@
+# 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
+
+# Context keys to remove on record creation.
+# This is needed to avoid values being set from context keys
+CONTEXT_KEYS_TO_REMOVE = [
+ "default_server_id",
+ "default_jet_template_id",
+ "default_plan_line_action_id",
+ "default_jet_id",
+ "default_server_template_id",
+]
+
+
+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"
+ )
+ jet_id = fields.Many2one(
+ comodel_name="cx.tower.jet",
+ string="Jet",
+ ondelete="cascade",
+ index=True,
+ )
+
+ jet_template_id = fields.Many2one(
+ comodel_name="cx.tower.jet.template",
+ string="Jet Template",
+ ondelete="cascade",
+ index=True,
+ )
+ 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_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!"
+ ),
+ ),
+ (
+ "unique_variable_value_jet_template",
+ "unique (variable_id, jet_template_id)",
+ "A variable value cannot be assigned multiple times to "
+ "the same jet template!",
+ ),
+ (
+ "unique_variable_value_jet",
+ "unique (variable_id, jet_id)",
+ "A variable value cannot be assigned multiple times to the same jet!",
+ ),
+ ]
+
+ # -- 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",
+ "jet_id",
+ "jet_template_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",
+ "jet_id",
+ "jet_template_id",
+ )
+ def _check_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)
+ + bool(record.jet_id)
+ + bool(record.jet_template_id)
+ )
+ if count_assigned > 1:
+ raise ValidationError(
+ _(
+ "Variable '%(var)s' can only be assigned to one of the models "
+ "at a time: "
+ "Server, Jet, Jet Template, Server Template, or "
+ "Plan Line Action.",
+ var=record.variable_id.name,
+ )
+ )
+
+ @api.constrains(
+ "server_id", "server_template_id", "jet_id", "jet_template_id", "variable_id"
+ )
+ def _check_unique_for_server_no_jet_no_jet_template(self):
+ """Ensure uniqueness of variable+server when both jet fields are empty"""
+ # Filter records that have both jet fields empty
+ records_to_check = self.filtered(
+ lambda r: not r.jet_id and not r.jet_template_id
+ )
+
+ if not records_to_check:
+ return
+
+ # Use read_group to find duplicates efficiently
+ domain = [
+ ("jet_id", "=", False),
+ ("jet_template_id", "=", False),
+ ("variable_id", "in", records_to_check.mapped("variable_id").ids),
+ ("server_id", "in", records_to_check.mapped("server_id").ids),
+ ]
+
+ grouped_data = self._read_group(
+ domain=domain,
+ groupby=["variable_id", "server_id"],
+ aggregates=["__count"],
+ )
+
+ # Odoo 17+: _read_group returns rows as
+ # (groupby_1, ..., aggregate_1, ...); many2one groups are recordsets.
+ for variable_rs, server_rs, row_count in grouped_data:
+ if row_count > 1:
+ variable_name = variable_rs.display_name if variable_rs else "Unknown"
+ server_name = server_rs.display_name if server_rs else "Unknown"
+ raise ValidationError(
+ _(
+ "Multiple records found with Variable '%(variable_name)s'"
+ " and Server '%(server_name)s' "
+ "with both Jet and Jet Template empty.",
+ variable_name=variable_name,
+ server_name=server_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
+ """
+ # Remove all 'default_' keys from context
+ # This is needed to avoid values being set from context keys
+ # Eg 'default_server_id' will set the server_id even if it's
+ # not provided in vals_list.
+ # This is a workaround to avoid the issue.
+
+ self = self._self_with_clean_context()
+
+ 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 _self_with_clean_context(self):
+ """
+ Clean context to avoid values being set from context keys
+
+ Returns:
+ self: with context cleaned
+ """
+ context = self.env.context.copy()
+ for key in CONTEXT_KEYS_TO_REMOVE:
+ context.pop(key, None)
+ return self.with_context(context) # pylint: disable=context-overridden
+
+ 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"),
+ "cx.tower.jet.template": ("jet_template_id", "Jet Template"),
+ "cx.tower.jet": ("jet_id", "Jet"),
+ }
+
+ 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
new file mode 100644
index 0000000..5fedf56
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_vault.py
@@ -0,0 +1,52 @@
+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
new file mode 100644
index 0000000..a26def6
--- /dev/null
+++ b/addons/cetmix_tower_server/models/cx_tower_vault_mixin.py
@@ -0,0 +1,416 @@
+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 _fetch_query(self, query, fields):
+ """Substitute fields based on api.
+
+ This method replaces values of secret fields with a placeholder value
+ when they are read from the database.
+
+ Args:
+ query (str): Query to fetch records
+ fields (list): List of fields to read
+ """
+ records = super()._fetch_query(query, fields)
+
+ # Replace secret field values with placeholders
+ for secret_field in self.SECRET_FIELDS:
+ if not fields or secret_field in [f.name for f in fields]:
+ # Use cache to set placeholder values without triggering field access
+ for record in records:
+ field = self._fields[secret_field]
+ self.env.cache.set(record, field, self.SECRET_VALUE_PLACEHOLDER)
+ return records
+
+ @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),))
+
+ def _is_secret_value_set(self, field_name):
+ """
+ Check if a secret value is set for a specific field for a single record.
+ This method is preferable to _get_secret_value because it doesn't require
+ to expose the secret value to the caller.
+
+ Args:
+ field_name (str): Name of the secret field to check
+
+ Returns:
+ bool: True if the secret value is set, False otherwise
+ """
+ return self._get_secret_value(field_name) is not None
diff --git a/addons/cetmix_tower_server/models/ir_actions_server.py b/addons/cetmix_tower_server/models/ir_actions_server.py
new file mode 100644
index 0000000..c3bd1d2
--- /dev/null
+++ b/addons/cetmix_tower_server/models/ir_actions_server.py
@@ -0,0 +1,24 @@
+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
new file mode 100644
index 0000000..36da516
--- /dev/null
+++ b/addons/cetmix_tower_server/models/res_config_settings.py
@@ -0,0 +1,79 @@
+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
new file mode 100644
index 0000000..053bba7
--- /dev/null
+++ b/addons/cetmix_tower_server/models/res_partner.py
@@ -0,0 +1,47 @@
+# 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,list,form",
+ "domain": [("partner_id", "child_of", self.id)],
+ "context": {"default_partner_id": self.id},
+ }
diff --git a/addons/cetmix_tower_server/models/res_users.py b/addons/cetmix_tower_server/models/res_users.py
new file mode 100644
index 0000000..5712739
--- /dev/null
+++ b/addons/cetmix_tower_server/models/res_users.py
@@ -0,0 +1,34 @@
+from odoo import fields, models
+
+
+class ResUsers(models.Model):
+ _inherit = "res.users"
+
+ USER_ACCESS_LEVEL = "1"
+ MANAGER_ACCESS_LEVEL = "2"
+ ROOT_ACCESS_LEVEL = "3"
+
+ cetmix_tower_show_jet_available_states = fields.Boolean(
+ help="Show available states in the jet view",
+ )
+
+ def _cetmix_tower_access_level(self):
+ """
+ Returns the access level of the current logged-in user
+ Not the record user!
+
+ Returns:
+ str: The access level of the user.
+ - "1": User
+ - "2": Manager
+ - "3": Root
+ False: No access
+ """
+
+ if self.env.user.has_group("cetmix_tower_server.group_root"):
+ return self.ROOT_ACCESS_LEVEL
+ if self.env.user.has_group("cetmix_tower_server.group_manager"):
+ return self.MANAGER_ACCESS_LEVEL
+ if self.env.user.has_group("cetmix_tower_server.group_user"):
+ return self.USER_ACCESS_LEVEL
+ return False
diff --git a/addons/cetmix_tower_server/models/tools.py b/addons/cetmix_tower_server/models/tools.py
new file mode 100644
index 0000000..a2475c4
--- /dev/null
+++ b/addons/cetmix_tower_server/models/tools.py
@@ -0,0 +1,75 @@
+# Copyright (C) 2022 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+from random import choices
+from urllib.parse import urlparse
+
+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)
+
+
+def is_valid_url(url: str, no_scheme_check: bool = False) -> bool:
+ """Check if a URL is valid.
+
+ Args:
+ url (str): URL to check
+ no_scheme_check (bool, optional):
+ If True, the scheme check will be skipped.
+ Defaults to False.
+ Returns:
+ bool: True if URL is valid, False otherwise
+ """
+ if not url:
+ return False
+
+ # Add dummy scheme if missing so urlparse works
+ if no_scheme_check:
+ if "://" not in url:
+ url = "http://" + url
+
+ parsed = urlparse(url)
+
+ # Must have a domain or IP
+ if not parsed.netloc:
+ return False
+
+ # Basic domain validation (at least one dot OR localhost OR IP)
+ host = parsed.hostname
+ if not host:
+ return False
+
+ if host in ("localhost", "::1"):
+ return True
+
+ if "." in host or ":" in host:
+ return True
+
+ return False
diff --git a/addons/cetmix_tower_server/pyproject.toml b/addons/cetmix_tower_server/pyproject.toml
new file mode 100644
index 0000000..4231d0c
--- /dev/null
+++ b/addons/cetmix_tower_server/pyproject.toml
@@ -0,0 +1,3 @@
+[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
new file mode 100644
index 0000000..8c717e5
--- /dev/null
+++ b/addons/cetmix_tower_server/readme/CONFIGURE.md
@@ -0,0 +1 @@
+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
new file mode 100644
index 0000000..51ca687
--- /dev/null
+++ b/addons/cetmix_tower_server/readme/DESCRIPTION.md
@@ -0,0 +1,4 @@
+[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
new file mode 100644
index 0000000..e04e277
--- /dev/null
+++ b/addons/cetmix_tower_server/readme/HISTORY.md
@@ -0,0 +1,49 @@
+## 18.0.2.0.0 (2026-04-07)
+
+- Features: Jets! (4700)
+
+
+## 18.0.1.0.11 (2026-03-10)
+
+- Bugfixes: Last flight plan line post-run action was not triggered correctly. (5120)
+
+
+## 18.0.1.0.10 (2026-03-10)
+
+- Features: Improve the 'File using template' command flow, fix the flight plan line view layout. (5197)
+
+
+## 18.0.1.0.9 (2026-02-19)
+
+- Features: Blacklist filter for Python commands, value checker for Vault. (5253)
+
+
+## 18.0.1.0.7 (2026-02-05)
+
+- Features: Scheduled tasks: allow to select specific days of week. (5190)
+
+- Bugfixes: Ensure custom values can be updated even if not provided initially. (5175)
+
+
+## 18.0.1.0.6 (2026-01-20)
+
+- Bugfixes: Make pre-defined messages and command help translatable again. (5174)
+
+
+## 18.0.1.0.4 (2025-12-23)
+
+- Bugfixes: Handle malformed expressions in flight plan line conditions. (5154)
+
+
+## 18.0.1.0.3 (2025-12-17)
+
+- Features: Parse empty or missing key values as 'None' instead of leaving key reference as is. (5134)
+- Features: Improve search views, implement the search panel for selected views. (5139)
+
+- Bugfixes: Custom values in flight plan are lost in a skipped command and are not available after it. (5129)
+
+
+## 18.0.1.0.2 (2025-12-08)
+
+- Bugfixes: Make variables selectable in scheduled tasks (5105)
+- Bugfixes: Save correct error message in log when SSH connection fails. (5109)
diff --git a/addons/cetmix_tower_server/readme/USAGE.md b/addons/cetmix_tower_server/readme/USAGE.md
new file mode 100644
index 0000000..901f5a6
--- /dev/null
+++ b/addons/cetmix_tower_server/readme/USAGE.md
@@ -0,0 +1 @@
+Please refer to the [official documentation](https://cetmix.com/tower) for detailed usage instructions.
diff --git a/addons/cetmix_tower_server/readme/diagrams/jets.puml b/addons/cetmix_tower_server/readme/diagrams/jets.puml
new file mode 100644
index 0000000..15e0c6e
--- /dev/null
+++ b/addons/cetmix_tower_server/readme/diagrams/jets.puml
@@ -0,0 +1,77 @@
+@startuml new_jet_flow
+title New Jet Flow
+
+start
+
+:New jet is created;
+:Build jet dependencies;
+while (For every dependency)
+ if (Required jet exists?) then (Yes)
+ if (In the required state?) then (Yes)
+ :Save jet in the dependency;
+ else (No)
+ :Create a jet request to bring\nthis jet to the required state;
+ endif
+ else (No)
+ :Create a jet request for a new jet;
+ endif
+endwhile
+
+stop
+@enduml
+
+
+@startuml jet_request_flow
+title Jet Request Flow
+|Requesting Jet|
+:Need for a jet in a specific state;
+
+if (Jet available?) then (Yes)
+ if (In the required state?) then (Yes)
+ if (Jet is busy?) then (no)
+ :Ask to go to the required state;
+ else (Yes)
+ :Create a jet request to bring\nthis jet to the required state;
+ |Requested Jet|
+ :Finalize the current operation;
+ :Check for pending requests;
+ if (Pending requests?) then (Yes)
+ while (Pending requests?)
+ :Process the request;
+ :Callback the request issuer;
+ endwhile
+ endif
+ endif
+ else (No)
+ :Ask to go to the required state;
+ endif
+else (No)
+ |Requesting Jet|
+ :Create a jet request for a new jet;
+endif
+stop
+@enduml
+
+@startuml jet_state_transition
+title Jet State Transition
+
+start
+
+:Update dependencies;
+if (All dependencies satisfied?) then (Yes)
+ while (Actions to reach the required state)
+ :Execute the action;
+ if (Pending requests?) then (Yes)
+ while (For every pending request)
+ :Process the request;
+ :Callback the request issuer;
+ endwhile
+endif
+
+ endwhile
+else (No)
+ :Wait for dependencies;
+endif
+
+stop
+@enduml
diff --git a/addons/cetmix_tower_server/readme/newsfragments/.gitkeep b/addons/cetmix_tower_server/readme/newsfragments/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/addons/cetmix_tower_server/security/cetmix_tower_server_groups.xml b/addons/cetmix_tower_server/security/cetmix_tower_server_groups.xml
new file mode 100644
index 0000000..015c2f6
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cetmix_tower_server_groups.xml
@@ -0,0 +1,38 @@
+
+
+
+ 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
new file mode 100644
index 0000000..9e8348c
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_command_log_security.xml
@@ -0,0 +1,33 @@
+
+
+
+ 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
new file mode 100644
index 0000000..515c0e2
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_command_security.xml
@@ -0,0 +1,84 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..6401136
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_file_security.xml
@@ -0,0 +1,52 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..4b43a07
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_file_template_security.xml
@@ -0,0 +1,50 @@
+
+
+
+
+ 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_jet_action_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_action_security.xml
new file mode 100644
index 0000000..f8dcfff
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_action_security.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+ Jet Action: User Read Access
+
+
+
+ [
+ ("access_level", "=", "1"),
+ "|",
+ ("jet_template_id.access_level", "=", "1"),
+ "|",
+ ("jet_template_id.user_ids", "in", [user.id]),
+ ("jet_template_id.jet_ids.user_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Action: Manager Read Access
+
+
+
+ [
+ ("access_level", "<=", "2"),
+ "|",
+ ("jet_template_id.access_level", "<=", "2"),
+ "|",
+ ("jet_template_id.user_ids", "in", [user.id]),
+ ("jet_template_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Action: Manager Write/Create/Unlink
+
+
+
+ [
+ ("access_level", "<=", "2"),
+ ("jet_template_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Action: Root Full Access
+
+
+ [(1, '=', 1)]
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_jet_dependency_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_dependency_security.xml
new file mode 100644
index 0000000..f82df90
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_dependency_security.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+ Jet Dependency: Manager Read Access
+
+
+
+ ["&",
+ "|",
+ ("jet_id.user_ids", "in", [user.id]),
+ ("jet_id.manager_ids", "in", [user.id]),
+ "|",
+ ("jet_depends_on_id.user_ids", "in", [user.id]),
+ ("jet_depends_on_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+ Jet Dependency: Manager write & create & unlink
+
+
+
+ [
+ ("jet_id.manager_ids", "in", [user.id]),
+ "|",
+ ("jet_depends_on_id.user_ids", "in", [user.id]),
+ ("jet_depends_on_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+ Jet Dependency: Root Full Access
+
+
+ [(1, '=', 1)]
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_jet_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_security.xml
new file mode 100644
index 0000000..b6590a3
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_security.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+ Jet: User Read Access
+
+
+
+ [
+ ("user_ids", "in", [user.id]),
+ ("server_id.user_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet: Manager Read Access
+
+
+
+ ["&",
+ "|",
+ ("user_ids", "in", [user.id]),
+ ("manager_ids", "in", [user.id]),
+ "|",
+ ("server_id.user_ids", "in", [user.id]),
+ ("server_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet: Manager write & create
+
+
+
+ [
+ ("manager_ids", "in", [user.id]),
+ "|",
+ ("server_id.user_ids", "in", [user.id]),
+ ("server_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet: Manager unlink
+
+
+
+ [
+ ("manager_ids", "in", [user.id]),
+ ("create_uid", "=", user.id),
+ "|",
+ ("server_id.user_ids", "in", [user.id]),
+ ("server_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+ Jet: Root Full Access
+
+
+ [(1, '=', 1)]
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_jet_template_dependency_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_template_dependency_security.xml
new file mode 100644
index 0000000..cdc52fb
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_template_dependency_security.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+ Jet Template Dependency: Manager Read Access
+
+
+
+ ["|",
+ ("template_id.access_level", "<=", "2"),
+ "|",
+ ("template_id.user_ids", "in", [user.id]),
+ ("template_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Template Dependency: Manager write & create & unlink
+
+
+
+ [("template_id.access_level", "<=", "2"), ("template_id.manager_ids", "in", [user.id])]
+
+
+
+
+
+
+
+
+
+ Jet Template Dependency: Root Full Access
+
+
+ [(1, '=', 1)]
+
+
+
+
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_jet_template_install_line_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_template_install_line_security.xml
new file mode 100644
index 0000000..c6bde1a
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_template_install_line_security.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Jet Template Install Line: Manager Read Access
+
+
+
+ ["&",
+ "|",
+ ("server_id.user_ids", "in", [user.id]),
+ ("server_id.manager_ids", "in", [user.id]),
+ "|",
+ ("jet_template_id.access_level", "<=", "2"),
+ ("jet_template_id.user_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+ Jet Template Install Line: Root Full Access
+
+
+ [(1, '=', 1)]
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_jet_template_install_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_template_install_security.xml
new file mode 100644
index 0000000..1cc8b2e
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_template_install_security.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Jet Template Install: Manager Read Access
+
+
+
+ ["&",
+ "|",
+ ("server_id.user_ids", "in", [user.id]),
+ ("server_id.manager_ids", "in", [user.id]),
+ "|",
+ ("jet_template_id.access_level", "<=", "2"),
+ ("jet_template_id.user_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+ Jet Template Install: Root Full Access
+
+
+ [(1, '=', 1)]
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_jet_template_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_template_security.xml
new file mode 100644
index 0000000..62c2590
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_template_security.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+ Jet Template: User Read Access
+
+
+
+ ["|","|",
+ ("access_level", "=", "1"),
+ ("user_ids", "in", [user.id]),
+ ("jet_ids.user_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Template: Manager Read Access
+
+
+
+ ["|",
+ ("access_level", "<=", "2"),
+ "|", "|",
+ ("user_ids", "in", [user.id]),
+ ("manager_ids", "in", [user.id]),
+ ("jet_ids.manager_ids", "in", [user.id])
+
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Template: Manager write & create
+
+
+
+ [("access_level", "<=", "2"), ("manager_ids", "in", [user.id])]
+
+
+
+
+
+
+
+
+
+
+ Jet Template: Manager unlink
+
+
+
+ [("access_level", "<=", "2"), ("manager_ids", "in", [user.id]), ("create_uid", "=", user.id)]
+
+
+
+
+
+
+
+
+
+ Jet Template: Root Full Access
+
+
+ [(1, '=', 1)]
+
+
+
+
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_jet_waypoint_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_waypoint_security.xml
new file mode 100644
index 0000000..54a21f7
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_waypoint_security.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+ Jet Waypoint: Manager Read Access
+
+
+
+ [("access_level", "<=", "2"),
+ "|",
+ ("jet_id.user_ids", "in", [user.id]),
+ ("jet_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Waypoint: Manager write & create
+
+
+
+ [("access_level", "<=", "2"),
+ ("jet_id.jet_template_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Waypoint: Manager unlink
+
+
+
+ [("access_level", "<=", "2"),
+ ("jet_id.jet_template_id.manager_ids", "in", [user.id]),
+ ("create_uid", "=", user.id)
+ ]
+
+
+
+
+
+
+
+
+
+ Jet Waypoint: Root Full Access
+
+
+ [(1, '=', 1)]
+
+
+
+
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_jet_waypoint_template_security.xml b/addons/cetmix_tower_server/security/cx_tower_jet_waypoint_template_security.xml
new file mode 100644
index 0000000..218e901
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_jet_waypoint_template_security.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+ Jet Waypoint Template: Manager Read Access
+
+
+
+ [("access_level", "<=", "2"),
+ "|",
+ ("jet_template_id.user_ids", "in", [user.id]),
+ ("jet_template_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Waypoint Template: Manager write & create
+
+
+
+ [("access_level", "<=", "2"),
+ ("jet_template_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+
+ Jet Waypoint Template: Manager unlink
+
+
+
+ [("access_level", "<=", "2"),
+ ("jet_template_id.manager_ids", "in", [user.id]),
+ ("create_uid", "=", user.id)
+ ]
+
+
+
+
+
+
+
+
+
+ Jet Waypoint Template: Root Full 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
new file mode 100644
index 0000000..5f53277
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_key_security.xml
@@ -0,0 +1,99 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..120309c
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_key_value_security.xml
@@ -0,0 +1,92 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..3427094
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_plan_line_action_security.xml
@@ -0,0 +1,90 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..21b2f87
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_plan_line_security.xml
@@ -0,0 +1,90 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..2d49e2a
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_plan_log_security.xml
@@ -0,0 +1,38 @@
+
+
+
+ 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
new file mode 100644
index 0000000..4ba61d1
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_plan_security.xml
@@ -0,0 +1,90 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..a5fb0e0
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_scheduled_task_cv_security.xml
@@ -0,0 +1,74 @@
+
+
+
+
+ Scheduled Task CV: manager read access
+
+
+
+
+
+
+
+ ['|',
+ '|',
+ ('scheduled_task_id.user_ids', 'in', [user.id]),
+ ('scheduled_task_id.manager_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_id.server_template_ids.user_ids', 'in', [user.id]),
+ ('scheduled_task_id.server_template_ids.manager_ids', 'in', [user.id]),
+ ('scheduled_task_id.jet_ids.user_ids', 'in', [user.id]),
+ ('scheduled_task_id.jet_ids.manager_ids', 'in', [user.id]),
+ ('scheduled_task_id.jet_template_ids.user_ids', 'in', [user.id]),
+ ('scheduled_task_id.jet_template_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
new file mode 100644
index 0000000..24feeaa
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_scheduled_task_security.xml
@@ -0,0 +1,78 @@
+
+
+
+
+ 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]),
+ ('jet_ids.user_ids', 'in', [user.id]),
+ ('jet_ids.manager_ids', 'in', [user.id]),
+ ('jet_template_ids.user_ids', 'in', [user.id]),
+ ('jet_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
new file mode 100644
index 0000000..9018763
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_server_log_security.xml
@@ -0,0 +1,204 @@
+
+
+
+
+ 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)]
+
+
+
+
+
+ Tower server log: user jet access rule
+
+
+ [
+ ("access_level", "=", "1"),
+ ("jet_id.user_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+ Tower server log: manager jet read access rule
+
+
+ [
+ ("access_level", "<=", "2"),
+ "|",
+ ("jet_id.user_ids", "in", [user.id]),
+ ("jet_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+ Tower server log: manager jet write access rule
+
+
+ [
+ ("access_level", "<=", "2"),
+ ("jet_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+ Tower server log: manager jet unlink access rule
+
+
+ [
+ ("access_level", "<=", "2"),
+ ("jet_id.manager_ids", "in", [user.id]),
+ ("create_uid", "=", user.id)
+ ]
+
+
+
+
+
+
+
+
+ Tower server log: user jet template access rule
+
+
+ [
+ ("access_level", "=", "1"),
+ ("jet_template_id.user_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+
+ Tower server log: manager jet template read access rule
+
+
+ [
+ ("access_level", "<=", "2"),
+ "|",
+ ("jet_template_id.user_ids", "in", [user.id]),
+ ("jet_template_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+ Tower server log: manager jet template write access rule
+
+
+ [
+ ("access_level", "<=", "2"),
+ ("jet_template_id.manager_ids", "in", [user.id])
+ ]
+
+
+
+
+
+
+
+ Tower server log: manager jet template unlink access rule
+
+
+ [
+ ("access_level", "<=", "2"),
+ ("jet_template_id.manager_ids", "in", [user.id]),
+ ("create_uid", "=", user.id)
+ ]
+
+
+
+
+
+
diff --git a/addons/cetmix_tower_server/security/cx_tower_server_security.xml b/addons/cetmix_tower_server/security/cx_tower_server_security.xml
new file mode 100644
index 0000000..f1f1247
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_server_security.xml
@@ -0,0 +1,74 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..628a104
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_server_template_security.xml
@@ -0,0 +1,54 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..bdc5ffd
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_server_wizard_access_rules.xml
@@ -0,0 +1,99 @@
+
+
+
+
+ Creator only
+
+
+ [('create_uid', '=', user.id)]
+
+
+
+
+ Creator only
+
+
+ [('create_uid', '=', user.id)]
+
+
+
+
+ Creator only
+
+
+ [('create_uid', '=', user.id)]
+
+
+
+
+ Creator only
+
+
+ [('create_uid', '=', user.id)]
+
+
+
+
+ Creator only
+
+
+ [('create_uid', '=', user.id)]
+
+
+
+
+ Creator only
+
+
+ [('create_uid', '=', user.id)]
+
+
+
+
+ Creator only
+
+
+ [('create_uid', '=', user.id)]
+
+
+
+
+ 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
new file mode 100644
index 0000000..b30603d
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_shortcut_security.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+ 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
new file mode 100644
index 0000000..d7c4a35
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_tag_security.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..c961071
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_variable_option_security.xml
@@ -0,0 +1,52 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..caf584a
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_variable_security.xml
@@ -0,0 +1,52 @@
+
+
+
+
+ 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
new file mode 100644
index 0000000..330f072
--- /dev/null
+++ b/addons/cetmix_tower_server/security/cx_tower_variable_value_security.xml
@@ -0,0 +1,102 @@
+
+
+
+
+ Variable Value: User Read Access
+
+ [
+ ('access_level', '=', '1'),
+ '|', '|', '|', '|', '|',
+ ('is_global', '=', True),
+ ('server_id.user_ids', 'in', [user.id]),
+ ('plan_line_action_id.plan_id.user_ids', 'in', [user.id]),
+ ('plan_line_action_id.plan_id.server_ids.user_ids', 'in', [user.id]),
+ ('jet_id.user_ids', 'in', [user.id]),
+ ('jet_template_id.user_ids', 'in', [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+ Variable Value: Manager Read Access
+
+ [
+ ('access_level', '<=', '2'),
+ '|', '|', '|', '|', '|', '|', '|', '|', '|', '|', '|', '|',
+ ('is_global', '=', True),
+ ('server_id.user_ids', 'in', [user.id]),
+ ('server_id.manager_ids', 'in', [user.id]),
+ ('server_template_id.user_ids', 'in', [user.id]),
+ ('server_template_id.manager_ids', 'in', [user.id]),
+ ('plan_line_action_id.plan_id.user_ids', 'in', [user.id]),
+ ('plan_line_action_id.plan_id.manager_ids', 'in', [user.id]),
+ ('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]),
+ ('jet_id.user_ids', 'in', [user.id]),
+ ('jet_id.manager_ids', 'in', [user.id]),
+ ('jet_template_id.user_ids', 'in', [user.id]),
+ ('jet_template_id.manager_ids', 'in', [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+ Variable Value: Manager Write/Create Access
+
+ [
+ ('access_level', '<=', '2'),
+ '|', '|', '|', '|', '|',
+ ('server_id.manager_ids', 'in', [user.id]),
+ ('server_template_id.manager_ids', 'in', [user.id]),
+ ('plan_line_action_id.plan_id.manager_ids', 'in', [user.id]),
+ ('plan_line_action_id.plan_id.server_ids.manager_ids', 'in', [user.id]),
+ ('jet_id.manager_ids', 'in', [user.id]),
+ ('jet_template_id.manager_ids', 'in', [user.id])
+ ]
+
+
+
+
+
+
+
+
+
+ Variable Value: Manager Unlink Access
+
+ [
+ ('access_level', '<=', '2'),
+ ('create_uid', '=', user.id),
+ '|', '|', '|', '|', '|',
+ ('server_id.manager_ids', 'in', [user.id]),
+ ('server_template_id.manager_ids', 'in', [user.id]),
+ ('plan_line_action_id.plan_id.manager_ids', 'in', [user.id]),
+ ('plan_line_action_id.plan_id.server_ids.manager_ids', 'in', [user.id]),
+ ('jet_id.manager_ids', 'in', [user.id]),
+ ('jet_template_id.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
new file mode 100644
index 0000000..31b20aa
--- /dev/null
+++ b/addons/cetmix_tower_server/security/ir.model.access.csv
@@ -0,0 +1,100 @@
+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
+access_cx_tower_jet_state_user,cx.tower.jet.state user,model_cx_tower_jet_state,group_user,1,0,0,0
+access_cx_tower_jet_state_manager,cx.tower.jet.state manager,model_cx_tower_jet_state,group_manager,1,0,0,0
+access_cx_tower_jet_state_root,cx.tower.jet.state root,model_cx_tower_jet_state,group_root,1,1,1,1
+access_cx_tower_jet_action_user,cx.tower.jet.action user,model_cx_tower_jet_action,group_user,1,0,0,0
+access_cx_tower_jet_action_manager,cx.tower.jet.action manager,model_cx_tower_jet_action,group_manager,1,1,1,1
+access_cx_tower_jet_action_root,cx.tower.jet.action root,model_cx_tower_jet_action,group_root,1,1,1,1
+access_cx_tower_jet_template_user,cx.tower.jet.template user,model_cx_tower_jet_template,group_user,1,0,0,0
+access_cx_tower_jet_template_manager,cx.tower.jet.template manager,model_cx_tower_jet_template,group_manager,1,1,1,1
+access_cx_tower_jet_template_dependency_manager,cx.tower.jet.template.dependency manager,model_cx_tower_jet_template_dependency,group_manager,1,1,1,1
+access_cx_tower_jet_template_install_manager,Jet Template Install->Manager,model_cx_tower_jet_template_install,group_manager,1,0,0,0
+access_cx_tower_jet_template_install_root,Jet Template Install->Root,model_cx_tower_jet_template_install,group_root,1,1,1,1
+access_cx_tower_jet_template_install_line_manager,Jet Template Install Line->Manager,model_cx_tower_jet_template_install_line,group_manager,1,0,0,0
+access_cx_tower_jet_template_install_line_root,Jet Template Install Line->Root,model_cx_tower_jet_template_install_line,group_root,1,1,1,1
+access_cx_tower_jet_template_install_wiz_manager,Jet Template Install Wizard->Manager,model_cx_tower_jet_template_install_wiz,group_manager,1,1,1,1
+access_cx_tower_jet_user,cx.tower.jet user,model_cx_tower_jet,group_user,1,0,0,0
+access_cx_tower_jet_manager,cx.tower.jet manager,model_cx_tower_jet,group_manager,1,1,1,1
+access_cx_tower_jet_root,cx.tower.jet root,model_cx_tower_jet,group_root,1,1,1,1
+access_cx_tower_jet_request_root,Jet Request->Root,model_cx_tower_jet_request,group_root,1,1,1,1
+access_cx_tower_jet_dependency_manager,cx.tower.jet.dependency manager,model_cx_tower_jet_dependency,group_manager,1,1,1,1
+access_cx_tower_jet_dependency_root,cx.tower.jet.dependency root,model_cx_tower_jet_dependency,group_root,1,1,1,1
+access_cx_tower_jet_state_wizard_user,Jet State Wizard->User,model_cx_tower_jet_state_wizard,group_user,1,1,1,1
+access_cx_tower_jet_action_wizard_user,Jet Action Wizard->User,model_cx_tower_jet_action_wizard,group_user,1,1,1,1
+access_cx_tower_jet_create_wizard_user,Jet Create Wizard->User,model_cx_tower_jet_create_wizard,group_user,1,1,1,1
+access_cx_tower_jet_create_wizard_variable_line_user,Jet Create Wizard Variable Line->User,model_cx_tower_jet_create_wizard_variable_line,group_user,1,1,1,1
+access_cx_tower_jet_clone_wizard_user,Jet Clone Wizard->User,model_cx_tower_jet_clone_wizard,group_user,1,1,1,1
+access_cx_tower_jet_clone_wizard_variable_line_user,Jet Clone Wizard Variable Line->User,model_cx_tower_jet_clone_wizard_variable_line,group_user,1,1,1,1
+access_cx_tower_jet_waypoint_template_manager,Jet Waypoint Template->Manager,model_cx_tower_jet_waypoint_template,group_manager,1,1,1,1
+access_cx_tower_jet_waypoint_template_root,Jet Waypoint Template->Root,model_cx_tower_jet_waypoint_template,group_root,1,1,1,1
+access_cx_tower_jet_waypoint_manager,Jet Waypoint->Manager,model_cx_tower_jet_waypoint,group_manager,1,1,1,1
+access_cx_tower_jet_waypoint_root,Jet Waypoint->Root,model_cx_tower_jet_waypoint,group_root,1,1,1,1
diff --git a/addons/cetmix_tower_server/ssh/__init__.py b/addons/cetmix_tower_server/ssh/__init__.py
new file mode 100644
index 0000000..c601950
--- /dev/null
+++ b/addons/cetmix_tower_server/ssh/__init__.py
@@ -0,0 +1 @@
+from . import ssh
diff --git a/addons/cetmix_tower_server/ssh/ssh.py b/addons/cetmix_tower_server/ssh/ssh.py
new file mode 100644
index 0000000..3478f87
--- /dev/null
+++ b/addons/cetmix_tower_server/ssh/ssh.py
@@ -0,0 +1,379 @@
+import io
+import logging
+import time
+
+_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) -> 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: str | None = None,
+ ssh_key: str | None = None,
+ host_key: str | None = 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: SSHClient | None = 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: SFTPClient | None = 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: 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: str | None = 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/demo/img/backup.png b/addons/cetmix_tower_server/static/demo/img/backup.png
new file mode 100644
index 0000000..c67452e
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/backup.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/clean.png b/addons/cetmix_tower_server/static/demo/img/clean.png
new file mode 100644
index 0000000..f759f4b
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/clean.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/docker.png b/addons/cetmix_tower_server/static/demo/img/docker.png
new file mode 100644
index 0000000..01e892d
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/docker.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/kubernetes.png b/addons/cetmix_tower_server/static/demo/img/kubernetes.png
new file mode 100644
index 0000000..db51b75
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/kubernetes.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/mariadb.png b/addons/cetmix_tower_server/static/demo/img/mariadb.png
new file mode 100644
index 0000000..1d9db21
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/mariadb.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/monitoring.png b/addons/cetmix_tower_server/static/demo/img/monitoring.png
new file mode 100644
index 0000000..27e846a
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/monitoring.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/nginx.png b/addons/cetmix_tower_server/static/demo/img/nginx.png
new file mode 100644
index 0000000..20ba9fb
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/nginx.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/odoo.png b/addons/cetmix_tower_server/static/demo/img/odoo.png
new file mode 100644
index 0000000..1834307
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/odoo.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/owncloud.png b/addons/cetmix_tower_server/static/demo/img/owncloud.png
new file mode 100644
index 0000000..0e0aeb5
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/owncloud.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/postgres.png b/addons/cetmix_tower_server/static/demo/img/postgres.png
new file mode 100644
index 0000000..7952062
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/postgres.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/proxmox.png b/addons/cetmix_tower_server/static/demo/img/proxmox.png
new file mode 100644
index 0000000..ed181b6
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/proxmox.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/test.png b/addons/cetmix_tower_server/static/demo/img/test.png
new file mode 100644
index 0000000..a6fa74d
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/test.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/tower.png b/addons/cetmix_tower_server/static/demo/img/tower.png
new file mode 100644
index 0000000..960bc4a
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/tower.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/traefik.png b/addons/cetmix_tower_server/static/demo/img/traefik.png
new file mode 100644
index 0000000..84fa783
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/traefik.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/woocommerce.png b/addons/cetmix_tower_server/static/demo/img/woocommerce.png
new file mode 100644
index 0000000..cc29df9
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/woocommerce.png differ
diff --git a/addons/cetmix_tower_server/static/demo/img/wordpress.png b/addons/cetmix_tower_server/static/demo/img/wordpress.png
new file mode 100644
index 0000000..4fe0d07
Binary files /dev/null and b/addons/cetmix_tower_server/static/demo/img/wordpress.png differ
diff --git a/addons/cetmix_tower_server/static/description/banner.png b/addons/cetmix_tower_server/static/description/banner.png
new file mode 100644
index 0000000..98c44a7
Binary files /dev/null and b/addons/cetmix_tower_server/static/description/banner.png differ
diff --git a/addons/cetmix_tower_server/static/description/icon.png b/addons/cetmix_tower_server/static/description/icon.png
new file mode 100644
index 0000000..2507f55
Binary files /dev/null and b/addons/cetmix_tower_server/static/description/icon.png 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
new file mode 100644
index 0000000..c1a7061
Binary files /dev/null and b/addons/cetmix_tower_server/static/description/images/server_from_template_auto_action.png 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
new file mode 100644
index 0000000..01001d1
Binary files /dev/null and b/addons/cetmix_tower_server/static/description/images/server_log_tab.png 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
new file mode 100644
index 0000000..96c7d33
Binary files /dev/null and b/addons/cetmix_tower_server/static/description/images/server_log_usage_1.png 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
new file mode 100644
index 0000000..9fed0cd
Binary files /dev/null and b/addons/cetmix_tower_server/static/description/images/server_log_usage_2.png 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
new file mode 100644
index 0000000..5a7db2f
Binary files /dev/null and b/addons/cetmix_tower_server/static/description/images/user_profile.png differ
diff --git a/addons/cetmix_tower_server/static/description/index.html b/addons/cetmix_tower_server/static/description/index.html
new file mode 100644
index 0000000..2d33748
--- /dev/null
+++ b/addons/cetmix_tower_server/static/description/index.html
@@ -0,0 +1,514 @@
+
+
+
+
+
+Cetmix Tower Server
+
+
+
+
+
Cetmix Tower Server
+
+
+
+
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.
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.
+ 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.
+
+
+
+
diff --git a/addons/cetmix_tower_server/wizards/__init__.py b/addons/cetmix_tower_server/wizards/__init__.py
new file mode 100644
index 0000000..0d9777b
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/__init__.py
@@ -0,0 +1,9 @@
+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
+from . import cx_tower_jet_template_install_wizard
+from . import cx_tower_jet_state_wizard
+from . import cx_tower_jet_action_wizard
+from . import cx_tower_jet_create_wizard
+from . import cx_tower_jet_clone_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
new file mode 100644
index 0000000..d74f881
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard.py
@@ -0,0 +1,564 @@
+# 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",
+ compute="_compute_server_ids",
+ readonly=False,
+ required=True,
+ store=True,
+ )
+ jet_ids = fields.Many2many(
+ "cx.tower.jet",
+ string="Jets",
+ help="Jets to run the command on",
+ )
+ 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",
+ store=True,
+ )
+ show_jets = fields.Boolean(
+ compute="_compute_show_jets",
+ 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("jet_ids")
+ def _compute_server_ids(self):
+ for rec in self:
+ if rec.jet_ids:
+ rec.server_ids = rec.jet_ids.server_id
+
+ @api.depends("server_ids", "jet_ids")
+ def _compute_show_servers(self):
+ for rec in self:
+ rec.show_servers = (
+ bool(rec.server_ids and len(rec.server_ids) > 1)
+ and not rec.jet_ids
+ and not rec.result
+ )
+
+ @api.depends("jet_ids")
+ def _compute_show_jets(self):
+ for rec in self:
+ rec.show_jets = bool(rec.jet_ids and len(rec.jet_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.
+ if record.jet_ids:
+ server_id = record.jet_ids[0].server_id
+ else:
+ server_id = record.server_ids[0]
+
+ # Get variable list
+ variables = record.get_variables()
+
+ # Get variable values
+ variable_values = self.env[
+ "cx.tower.variable"
+ ]._get_variable_values_by_references(
+ variables.get(str(record.id)),
+ server=server_id,
+ jet_template=record.jet_ids[0].jet_template_id
+ if record.jet_ids
+ else None,
+ jet=record.jet_ids[0] if record.jet_ids else None,
+ )
+ 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.update(custom_vals)
+
+ # Render template
+ if variable_values:
+ record.rendered_code = record.render_code(
+ pythonic_mode=record.action == "python_code",
+ **variable_values,
+ )[record.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 code change
+ """
+
+ self.ensure_one()
+ # Remove existing custom variable values
+ self.custom_variable_value_ids = False
+
+ if (
+ self.jet_ids
+ or 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 = self.server_ids[0]
+
+ # Get variable list
+ variables = self.get_variables()
+
+ # Get variable values
+ variable_values = self.env[
+ "cx.tower.variable"
+ ]._get_variable_values_by_references(
+ variables.get(str(self.id)),
+ server=server._origin if hasattr(server, "_origin") else server,
+ )
+
+ # 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.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()
+ if self.jet_ids:
+ context["default_jet_ids"] = self.jet_ids.ids
+ else:
+ context["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 or jets"""
+ self.ensure_one()
+
+ # 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
+ },
+ }
+ if self.jet_ids:
+ for jet in self.jet_ids:
+ jet.run_command(
+ command=self.command_id,
+ sudo=self.use_sudo,
+ path=path_value,
+ **kwargs,
+ )
+ else:
+ for server in self.server_ids:
+ server.run_command(
+ 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": "list,form",
+ "target": "current",
+ "context": {"search_default_label": log_label},
+ }
+
+ def run_command_in_wizard(self):
+ """
+ Runs a given code as is in wizard
+ """
+ self.ensure_one()
+
+ # Check if multiple servers are selected
+ if len(self.server_ids) > 1:
+ raise ValidationError(
+ _("You cannot run custom code on multiple servers at once.")
+ )
+
+ # Check if multiple jets are selected
+ if len(self.jet_ids) > 1:
+ raise ValidationError(
+ _("You cannot run custom code on multiple jets at once.")
+ )
+
+ # From now we have one server or one jet selected
+ # Raise access error if non manager is trying to call this method
+ if not self._is_privileged_user():
+ raise AccessError(_("You are not allowed to execute commands in wizard"))
+
+ # Check if jet is currently executing an action
+ if self.jet_ids and self.jet_ids.current_action_id:
+ raise ValidationError(
+ _(
+ "Jet '%(jet)s' is currently executing an action",
+ jet=self.jet_ids.display_name,
+ )
+ )
+
+ 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
+ no_split_for_sudo = bool(self.command_id and self.command_id.no_split_for_sudo)
+
+ 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,
+ "log": {
+ "jet_id": self.jet_ids and self.jet_ids[0].id
+ if self.jet_ids
+ else None,
+ "jet_template_id": self.jet_ids
+ and self.jet_ids[0].jet_template_id.id
+ if self.jet_ids
+ else None,
+ },
+ }
+
+ 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
new file mode 100644
index 0000000..83fbc5d
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_command_run_wizard_view.xml
@@ -0,0 +1,213 @@
+
+
+
+ cx.tower.command.run.wizard.view.form
+ cx.tower.command.run.wizard
+
+
+
+
+
+ Remember: Python code is executed on the Tower server, not on the remote one.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sudo
+
+
+
+
+
+
+
+
+
+
+
+
+ Fill in required configuration variables on the “Configuration Values” tab before you can run the command.
+
+
+
+
+
+
+ You need 'Manager' access to the server to override the default configuration values.
+ Without this access, the server's configured values will be used.
+
+
+ Only values that the current user has access to are shown.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cetmix Tower Run Command
+ cx.tower.command.run.wizard
+ ir.actions.act_window
+ form
+
+ {'default_server_ids': [id]}
+ new
+
+
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_action_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_jet_action_wizard.py
new file mode 100644
index 0000000..979c185
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_action_wizard.py
@@ -0,0 +1,59 @@
+# Copyright (C) 2024 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models
+
+
+class CxTowerJetActionWizard(models.TransientModel):
+ """
+ Wizard to trigger jet actions.
+ """
+
+ _name = "cx.tower.jet.action.wizard"
+ _description = "Trigger Jet Action Wizard"
+
+ action_id = fields.Many2one(
+ comodel_name="cx.tower.jet.action",
+ required=True,
+ domain="[('id', 'in', action_available_ids)]",
+ )
+
+ jet_ids = fields.Many2many(
+ comodel_name="cx.tower.jet",
+ readonly=True,
+ )
+
+ action_available_ids = fields.Many2many(
+ comodel_name="cx.tower.jet.action",
+ compute="_compute_available_actions",
+ help="Actions that are available for all selected jets",
+ )
+
+ @api.depends("jet_ids")
+ def _compute_available_actions(self):
+ """Compute available actions based on selected jets"""
+ for wizard in self:
+ if not wizard.jet_ids:
+ wizard.action_available_ids = False
+ continue
+
+ # Get actions that are available to ALL selected jets
+ # Start with the first jet's available actions
+ first_jet = wizard.jet_ids[0]
+ available_actions = first_jet.action_available_ids
+
+ # Intersect with actions available to all other jets
+ for jet in wizard.jet_ids[1:]:
+ available_actions &= jet.action_available_ids
+
+ wizard.action_available_ids = available_actions
+
+ def action_confirm(self):
+ """Trigger the action for the selected jets"""
+ for wizard in self:
+ if wizard.jet_ids and wizard.action_id:
+ for jet in wizard.jet_ids:
+ jet._trigger_action(wizard.action_id)
+ return {
+ "type": "ir.actions.act_window_close",
+ }
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_action_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_jet_action_wizard_view.xml
new file mode 100644
index 0000000..b567c28
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_action_wizard_view.xml
@@ -0,0 +1,44 @@
+
+
+
+ cx.tower.jet.action.wizard.view.form
+ cx.tower.jet.action.wizard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Trigger Action
+ cx.tower.jet.action.wizard
+ form
+ new
+
+ list
+ {'default_jet_ids': active_ids}
+
+
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_clone_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_jet_clone_wizard.py
new file mode 100644
index 0000000..0fd5acd
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_clone_wizard.py
@@ -0,0 +1,140 @@
+# Copyright (C) 2025 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models
+
+
+class CxTowerJetCloneWizard(models.TransientModel):
+ """Clone jet"""
+
+ _name = "cx.tower.jet.clone.wizard"
+ _description = "Clone jet"
+
+ jet_id = fields.Many2one(
+ "cx.tower.jet",
+ required=True,
+ readonly=True,
+ )
+ jet_template_id = fields.Many2one(
+ "cx.tower.jet.template",
+ related="jet_id.jet_template_id",
+ readonly=True,
+ )
+ same_server = fields.Selection(
+ selection=[("y", "Yes"), ("n", "No")],
+ default="y",
+ required=True,
+ )
+ server_id = fields.Many2one(
+ "cx.tower.server",
+ domain="[('jet_template_ids', 'in', jet_template_id)]",
+ )
+ partner_id = fields.Many2one(
+ "res.partner",
+ compute="_compute_partner_id",
+ store=True,
+ readonly=False,
+ help="Partner associated with the cloned jet",
+ )
+ name = fields.Char(help="The name of the new jet")
+ name_type = fields.Selection(
+ selection=[("a", "will be auto-generated"), ("m", "I will put myself")],
+ default="a",
+ required=True,
+ )
+ url = fields.Char(help="The URL of the jet")
+ url_type = fields.Selection(
+ selection=[("a", "will be auto-generated"), ("m", "I will put myself")],
+ default="a",
+ required=True,
+ )
+ state_id = fields.Many2one(
+ "cx.tower.jet.state", required=True, help="Requested state of the jet"
+ )
+ state_domain = fields.Binary(compute="_compute_state_domain")
+ use_custom_variables = fields.Selection(
+ selection=[("n", "default settings"), ("y", "custom settings")],
+ default="n",
+ required=True,
+ )
+ line_ids = fields.One2many(
+ "cx.tower.jet.clone.wizard.variable.line",
+ "wizard_id",
+ string="Variable Lines",
+ )
+
+ @api.depends("jet_id")
+ def _compute_partner_id(self):
+ """
+ Compute the partner associated with the cloned jet
+ """
+ for wizard in self:
+ if wizard.partner_id:
+ continue
+ if wizard.jet_id and wizard.jet_id.partner_id:
+ wizard.partner_id = wizard.jet_id.partner_id.id
+
+ @api.depends("jet_template_id")
+ def _compute_state_domain(self):
+ """
+ Compute the domain for the states
+ """
+ for wizard in self:
+ if not wizard.jet_id:
+ wizard.state_domain = []
+ continue
+ wizard.state_domain = [
+ ("id", "in", wizard.jet_template_id.action_ids.state_to_id.ids)
+ ]
+
+ def action_confirm(self):
+ """
+ Clone the jet
+ """
+ self.ensure_one()
+ kwargs = {}
+
+ # Add custom variables
+ custom_variables = {}
+ if self.line_ids:
+ custom_variables = {
+ line.variable_id.reference: line.value_char for line in self.line_ids
+ }
+ if custom_variables:
+ kwargs["variable_values"] = custom_variables
+
+ # Add partner
+ if self.partner_id:
+ kwargs["partner_id"] = self.partner_id.id
+
+ # Add url
+ if self.url_type == "m" and self.url:
+ kwargs["url"] = self.url
+
+ jet = self.jet_id.clone(
+ server=self.server_id,
+ name=self.name,
+ state=self.state_id,
+ **kwargs,
+ )
+ return {
+ "type": "ir.actions.act_window",
+ "res_model": "cx.tower.jet",
+ "res_id": jet.id,
+ "view_mode": "form",
+ "target": "current",
+ }
+
+
+class CxTowerJetCloneWizardVariableLine(models.TransientModel):
+ """Custom variable values for jet create wizard"""
+
+ _name = "cx.tower.jet.clone.wizard.variable.line"
+ _inherit = "cx.tower.custom.variable.value.mixin"
+ _description = "Variable lines"
+
+ wizard_id = fields.Many2one("cx.tower.jet.clone.wizard")
+ # Override from mixin to make variable_id editable
+ variable_id = fields.Many2one(
+ readonly=False,
+ )
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_clone_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_jet_clone_wizard_view.xml
new file mode 100644
index 0000000..b7309bd
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_clone_wizard_view.xml
@@ -0,0 +1,111 @@
+
+
+
+ cx.tower.jet.clone.wizard.view.form
+ cx.tower.jet.clone.wizard
+
+
+
+
+
+
+ for
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Clone
+ cx.tower.jet.clone.wizard
+ form
+ new
+
+ form
+ {'default_jet_id': active_id}
+
+
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard.py
new file mode 100644
index 0000000..e49a632
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard.py
@@ -0,0 +1,206 @@
+# Copyright (C) 2025 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 CxTowerJetCreateWizard(models.TransientModel):
+ """Create new jet from template"""
+
+ _name = "cx.tower.jet.create.wizard"
+ _description = "Create new jet"
+
+ name = fields.Char(help="The name of the jet")
+ name_type = fields.Selection(
+ selection=[("a", "will be auto-generated"), ("m", "I will put myself")],
+ default="a",
+ required=True,
+ )
+ note = fields.Text(related="jet_template_id.note", readonly=True)
+ url = fields.Char(help="The URL of the jet")
+ url_type = fields.Selection(
+ selection=[("a", "will be auto-generated"), ("m", "I will put myself")],
+ default="a",
+ required=True,
+ )
+ partner_id = fields.Many2one(
+ "res.partner",
+ compute="_compute_partner_id",
+ store=True,
+ readonly=False,
+ help="Partner associated with the jet",
+ )
+ jet_template_id = fields.Many2one(
+ "cx.tower.jet.template",
+ required=True,
+ )
+ jet_template_domain = fields.Binary(
+ compute="_compute_jet_template_domain",
+ help="Domain for jet template",
+ )
+ jet_template_message = fields.Text(
+ compute="_compute_jet_template_domain",
+ help="Message for the user",
+ )
+ server_domain = fields.Binary(
+ compute="_compute_server_domain",
+ help="Domain for server",
+ )
+ server_id = fields.Many2one(
+ "cx.tower.server",
+ )
+ state_id = fields.Many2one("cx.tower.jet.state", help="Requested state of the jet")
+ state_domain = fields.Binary(compute="_compute_state_domain")
+ use_custom_variables = fields.Selection(
+ selection=[("n", "default settings"), ("y", "custom settings")],
+ default="n",
+ required=True,
+ )
+ line_ids = fields.One2many(
+ "cx.tower.jet.create.wizard.variable.line",
+ "wizard_id",
+ string="Variable Lines",
+ )
+
+ @api.depends("server_id")
+ def _compute_partner_id(self):
+ """
+ Compute the partner associated with the jet
+ """
+ for wizard in self:
+ # Do not modify partner if it is already set
+ if wizard.partner_id:
+ continue
+ # Set partner from server
+ if wizard.server_id and wizard.server_id.partner_id:
+ wizard.partner_id = wizard.server_id.partner_id.id
+
+ @api.depends("server_id")
+ def _compute_jet_template_domain(self):
+ """
+ Compute the domain and message for the jet templates
+ """
+ template_obj = self.env["cx.tower.jet.template"]
+ all_templates_domain = [("show_in_create_wizard", "=", True)]
+ all_templates = template_obj.search(all_templates_domain)
+ for wizard in self:
+ if not all_templates:
+ wizard.jet_template_message = _(
+ "No jet templates are currently configured as 'Show in Wizard'."
+ " Please check your jet template settings."
+ )
+ wizard.jet_template_domain = all_templates_domain
+ continue
+ if not wizard.server_id:
+ # All templates that can be shown in the create wizard
+ jet_template_message = False
+ jet_template_domain = all_templates_domain
+ else:
+ # All templates that can be shown in the create wizard and
+ # are installed on the selected server
+ jet_template_domain = [
+ ("show_in_create_wizard", "=", True),
+ ("server_ids", "in", wizard.server_id.ids),
+ ]
+ available_templates = all_templates.filtered_domain(jet_template_domain)
+ if not available_templates:
+ jet_template_message = _(
+ "No jet templates configured as 'Show in Wizard' are"
+ " installed on the selected server."
+ " Please check your jet template settings."
+ )
+ else:
+ jet_template_message = False
+
+ # Set the domain and message
+ wizard.jet_template_domain = jet_template_domain
+ wizard.jet_template_message = jet_template_message
+
+ @api.depends("jet_template_id")
+ def _compute_server_domain(self):
+ """
+ Compute the domain for the servers
+ """
+ for wizard in self:
+ if not wizard.jet_template_id:
+ wizard.server_domain = []
+ continue
+ wizard.server_domain = [("id", "in", wizard.jet_template_id.server_ids.ids)]
+
+ @api.depends("jet_template_id")
+ def _compute_state_domain(self):
+ """
+ Compute the domain for the states
+ """
+ for wizard in self:
+ if not wizard.jet_template_id:
+ wizard.state_domain = []
+ continue
+ wizard.state_domain = [
+ ("id", "in", wizard.jet_template_id.action_ids.state_to_id.ids)
+ ]
+
+ def action_confirm(self):
+ """
+ Create a new jet
+ """
+ self.ensure_one()
+
+ # Check if server is selected
+ if not self.server_id:
+ raise ValidationError(_("Please select a server to create a jet."))
+
+ kwargs = {}
+
+ # Add custom variables
+ variable_values = {}
+ if self.use_custom_variables == "y" and self.line_ids:
+ variable_values = {
+ line.variable_id.reference: line.value_char for line in self.line_ids
+ }
+ kwargs["variable_values"] = variable_values
+
+ # Add partner
+ if self.partner_id:
+ kwargs["partner_id"] = self.partner_id.id
+
+ # Add url
+ if self.url_type == "m" and self.url:
+ kwargs["url"] = self.url
+
+ jet = self.jet_template_id.create_jet(
+ self.server_id,
+ name=self.name,
+ state=self.state_id,
+ **kwargs,
+ )
+ if not jet:
+ raise ValidationError(
+ _(
+ "Failed to create jet. "
+ "Please check the server and template settings."
+ )
+ )
+
+ return {
+ "type": "ir.actions.act_window",
+ "res_model": "cx.tower.jet",
+ "res_id": jet.id,
+ "view_mode": "form",
+ "target": "current",
+ }
+
+
+class CxTowerJetCreateWizardVariableLine(models.TransientModel):
+ """Custom variable values for jet create wizard"""
+
+ _name = "cx.tower.jet.create.wizard.variable.line"
+ _inherit = "cx.tower.custom.variable.value.mixin"
+ _description = "Variable lines"
+
+ wizard_id = fields.Many2one("cx.tower.jet.create.wizard")
+ # Override from mixin to make variable_id editable
+ variable_id = fields.Many2one(
+ readonly=False,
+ )
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard_view.xml
new file mode 100644
index 0000000..2675f5d
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_create_wizard_view.xml
@@ -0,0 +1,132 @@
+
+
+
+ cx.tower.jet.create.wizard.view.form
+ cx.tower.jet.create.wizard
+
+
+
+
+
+
+
+
+
+
+ for
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Launch New Jet
+ cx.tower.jet.create.wizard
+ form
+ new
+
+
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_state_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_jet_state_wizard.py
new file mode 100644
index 0000000..1b50e7b
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_state_wizard.py
@@ -0,0 +1,79 @@
+# Copyright (C) 2024 Cetmix OÜ
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models
+
+
+class CxTowerJetStateWizard(models.TransientModel):
+ """
+ Wizard to set state for selected jets.
+ """
+
+ _name = "cx.tower.jet.state.wizard"
+ _description = "Set Jet State Wizard"
+
+ jet_ids = fields.Many2many(
+ comodel_name="cx.tower.jet",
+ string="Jets",
+ required=True,
+ readonly=True,
+ )
+
+ state_id = fields.Many2one(
+ comodel_name="cx.tower.jet.state",
+ required=True,
+ domain="[('id', 'in', available_state_ids)]",
+ )
+
+ available_state_ids = fields.Many2many(
+ comodel_name="cx.tower.jet.state",
+ string="Available States",
+ compute="_compute_available_states",
+ help="States that appear in the 'state_to' field "
+ "of jet templates of all selected jets",
+ )
+
+ @api.depends("jet_ids", "jet_ids.jet_template_id.action_ids.state_to_id")
+ def _compute_available_states(self):
+ """Compute available states based on selected jets' templates"""
+
+ # Used as a placeholder for no available states
+ state_obj = self.env["cx.tower.jet.state"]
+
+ for wizard in self:
+ if not wizard.jet_ids:
+ wizard.available_state_ids = False
+ continue
+
+ # Get states that are available to ALL selected jets
+ # Start with the first jet's available states
+ first_jet = wizard.jet_ids[0]
+ if not first_jet.jet_template_id.action_ids:
+ wizard.available_state_ids = False
+ continue
+
+ available_states = first_jet.jet_template_id.action_ids.mapped(
+ "state_to_id"
+ )
+
+ # Intersect with states available to all other jets
+ for jet in wizard.jet_ids[1:]:
+ actions = jet.jet_template_id.action_ids
+ # If no actions, no available states
+ if not actions:
+ available_states = state_obj
+ break
+ jet_states = actions.mapped("state_to_id")
+ available_states &= jet_states
+
+ # Remove current state from available states if only one jet is selected
+ if len(wizard.jet_ids) == 1:
+ available_states -= wizard.jet_ids.state_id
+ wizard.available_state_ids = available_states
+
+ def action_confirm(self):
+ """Bring the jets to the target state"""
+ for wizard in self:
+ if wizard.jet_ids and wizard.state_id:
+ for jet in wizard.jet_ids:
+ jet.bring_to_state(wizard.state_id.reference)
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_state_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_jet_state_wizard_view.xml
new file mode 100644
index 0000000..f8f1dd9
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_state_wizard_view.xml
@@ -0,0 +1,51 @@
+
+
+
+ cx.tower.jet.state.wizard.view.form
+ cx.tower.jet.state.wizard
+
+
+
+
+ Select a state to set for the selected jets. Only states that appear in the
+ "State to" field of jet templates of all selected jets are available.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Bring to State
+ cx.tower.jet.state.wizard
+ form
+ new
+
+ list
+ {'default_jet_ids': active_ids}
+
+
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_template_install_wizard.py b/addons/cetmix_tower_server/wizards/cx_tower_jet_template_install_wizard.py
new file mode 100644
index 0000000..f53a636
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_template_install_wizard.py
@@ -0,0 +1,72 @@
+# Copyright 2025 Cetmix OÜ
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0)
+
+from odoo import api, fields, models
+
+
+class CxTowerJetTemplateInstallWizard(models.TransientModel):
+ """
+ Wizard to install a Jet Template on selected servers.
+ """
+
+ _name = "cx.tower.jet.template.install.wiz"
+ _description = "Install Jet Template on Selected Servers"
+
+ jet_template_id = fields.Many2one(
+ "cx.tower.jet.template",
+ required=True,
+ )
+ server_ids = fields.Many2many(
+ "cx.tower.server",
+ string="Servers",
+ )
+ jet_template_domain = fields.Binary(
+ compute="_compute_jet_template_domain",
+ )
+ server_domain = fields.Binary(
+ compute="_compute_server_domain",
+ )
+
+ @api.depends("server_ids", "server_ids.jet_template_ids")
+ def _compute_jet_template_domain(self):
+ """
+ Show only templates that are not installed on the selected server.
+ """
+ for wizard in self:
+ if wizard.server_ids and len(wizard.server_ids) == 1:
+ server = wizard.server_ids[0]
+ templates_installed = server.jet_template_ids
+ wizard.jet_template_domain = [("id", "not in", templates_installed.ids)]
+ else:
+ wizard.jet_template_domain = []
+
+ @api.depends("jet_template_id", "jet_template_id.server_ids")
+ def _compute_server_domain(self):
+ """
+ Show only servers where the template is not installed.
+ """
+ for wizard in self:
+ if wizard.jet_template_id:
+ servers_installed = wizard.jet_template_id.server_ids
+ wizard.server_domain = (
+ [("id", "not in", servers_installed.ids)]
+ if servers_installed
+ else []
+ )
+ else:
+ wizard.server_domain = []
+
+ def action_install_template(self):
+ """
+ Install the Jet Template on the selected servers.
+ """
+ if self.server_ids:
+ self.jet_template_id.install_on_servers(self.server_ids)
+
+ # Close the wizard
+ return {
+ "type": "ir.actions.act_window_close",
+ "params": {
+ "next": {"type": "ir.actions.client", "tag": "soft_reload"},
+ },
+ }
diff --git a/addons/cetmix_tower_server/wizards/cx_tower_jet_template_install_wizard_view.xml b/addons/cetmix_tower_server/wizards/cx_tower_jet_template_install_wizard_view.xml
new file mode 100644
index 0000000..5f497e5
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_jet_template_install_wizard_view.xml
@@ -0,0 +1,49 @@
+
+
+ cx.tower.jet.template.install.wiz.form
+ cx.tower.jet.template.install.wiz
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Install on Servers
+ cx.tower.jet.template.install.wiz
+ ir.actions.act_window
+ form
+
+ {'default_jet_template_id': active_id}
+ new
+
+ form,list
+
+
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
new file mode 100644
index 0000000..4a17b08
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard.py
@@ -0,0 +1,178 @@
+# 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",
+ required=True,
+ compute="_compute_server_ids",
+ readonly=False,
+ store=True,
+ )
+ jet_ids = fields.Many2many(
+ "cx.tower.jet",
+ string="Jets",
+ )
+ 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",
+ store=True,
+ )
+ show_jets = fields.Boolean(
+ compute="_compute_show_jets",
+ 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("jet_ids")
+ def _compute_server_ids(self):
+ for rec in self:
+ if rec.jet_ids:
+ rec.server_ids = rec.jet_ids.server_id
+
+ @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) and not rec.jet_ids
+ )
+
+ @api.depends("jet_ids")
+ def _compute_show_jets(self):
+ for rec in self:
+ rec.show_jets = bool(rec.jet_ids and len(rec.jet_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,
+ }
+ if self.jet_ids:
+ for jet in self.jet_ids:
+ jet.run_flight_plan(self.plan_id, **custom_values)
+ else:
+ 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": "list,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
new file mode 100644
index 0000000..c165784
--- /dev/null
+++ b/addons/cetmix_tower_server/wizards/cx_tower_plan_run_wizard_view.xml
@@ -0,0 +1,112 @@
+
+
+
+ cx.tower.plan.run.wizard.view.form
+ cx.tower.plan.run.wizard
+
+