Tower: upload cetmix_tower_server 16.0.2.2.9 (via marketplace)
This commit is contained in:
283
addons/cetmix_tower_server/models/cx_tower_variable_mixin.py
Normal file
283
addons/cetmix_tower_server/models/cx_tower_variable_mixin.py
Normal file
@@ -0,0 +1,283 @@
|
||||
# Copyright (C) 2022 Cetmix OÜ
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
import logging
|
||||
import re
|
||||
import uuid
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TowerVariableMixin(models.AbstractModel):
|
||||
"""Used to implement variables and variable values.
|
||||
Inherit in your model if you want to use variables in it.
|
||||
"""
|
||||
|
||||
_name = "cx.tower.variable.mixin"
|
||||
_description = "Tower Variables mixin"
|
||||
|
||||
variable_value_ids = fields.One2many(
|
||||
string="Variable Values",
|
||||
comodel_name="cx.tower.variable.value",
|
||||
auto_join=True,
|
||||
help="Variable values for selected record",
|
||||
)
|
||||
|
||||
def get_variable_values(self, variable_references, apply_modifiers=True):
|
||||
"""Get variable values for selected records
|
||||
|
||||
Args:
|
||||
variable_references (list of Char): variable names
|
||||
apply_modifiers (bool): apply Python modifiers to the values
|
||||
|
||||
Returns:
|
||||
dict {record_id: {variable_reference: value}}
|
||||
"""
|
||||
res = {}
|
||||
|
||||
# Get global values first
|
||||
if variable_references:
|
||||
global_values = self.get_global_variable_values(variable_references)
|
||||
|
||||
# Get record wise values
|
||||
for rec in self:
|
||||
res_vars = global_values.get(
|
||||
rec.id, {}
|
||||
) # set global values as defaults
|
||||
for variable_reference in variable_references:
|
||||
# Check if this is a system variable
|
||||
system_value = self._get_system_variable_value(variable_reference)
|
||||
if system_value:
|
||||
res_vars.update({variable_reference: system_value})
|
||||
|
||||
# Get regular value
|
||||
else:
|
||||
value = rec.variable_value_ids.filtered(
|
||||
lambda v,
|
||||
variable_reference=variable_reference: v.variable_reference
|
||||
== variable_reference
|
||||
)
|
||||
if value:
|
||||
res_vars.update({variable_reference: value.value_char})
|
||||
|
||||
res.update({rec.id: res_vars})
|
||||
|
||||
# Final render
|
||||
# Render templates in values
|
||||
for variable_values in res.values():
|
||||
self._render_variable_values(variable_values)
|
||||
|
||||
# Apply modifiers
|
||||
if apply_modifiers:
|
||||
self._apply_modifiers(res)
|
||||
return res
|
||||
|
||||
def get_global_variable_values(self, variable_references):
|
||||
"""Get global values for variables.
|
||||
Such values do not belong to any record.
|
||||
|
||||
This function is used by get_variable_values()
|
||||
to compute fallback values.
|
||||
|
||||
Args:
|
||||
variable_references (list of Char): variable names
|
||||
|
||||
Returns:
|
||||
dict {record_id: {variable_reference: value}}
|
||||
"""
|
||||
res = {}
|
||||
|
||||
if variable_references:
|
||||
values = self.env["cx.tower.variable.value"].search(
|
||||
self._compose_variable_global_values_domain(variable_references)
|
||||
)
|
||||
for rec in self:
|
||||
res_vars = {}
|
||||
for variable_reference in variable_references:
|
||||
# Get variable value
|
||||
value = values.filtered(
|
||||
lambda v,
|
||||
variable_reference=variable_reference: v.variable_reference
|
||||
== variable_reference
|
||||
)
|
||||
res_vars.update(
|
||||
{variable_reference: value.value_char if value else None}
|
||||
)
|
||||
res.update({rec.id: res_vars})
|
||||
return res
|
||||
|
||||
def _get_system_variable_value(self, variable_reference):
|
||||
"""Get the value of a system variable. Eg `tower.server.partner_name`
|
||||
|
||||
Args:
|
||||
variable_reference (Char): variable value
|
||||
|
||||
Returns:
|
||||
dict(): populates `tower` variable with with values.
|
||||
{
|
||||
'server': {..server vals..},
|
||||
'tools': {..helper tools vals...}
|
||||
}.
|
||||
"""
|
||||
|
||||
# This works for a single record only!
|
||||
self.ensure_one()
|
||||
|
||||
variable_value = {}
|
||||
if variable_reference == "tower":
|
||||
variable_value.update(
|
||||
{
|
||||
"server": self._parse_system_variable_server(),
|
||||
"tools": self._parse_system_variable_tools(),
|
||||
}
|
||||
)
|
||||
|
||||
return variable_value
|
||||
|
||||
def _parse_system_variable_server(self):
|
||||
"""Parser system variable of `server` type.
|
||||
|
||||
Returns:
|
||||
dict(): `server` values of the `tower` variable.
|
||||
"""
|
||||
# Get current server
|
||||
values = {}
|
||||
server = self._get_current_server()
|
||||
if server:
|
||||
values = {
|
||||
"name": server.name,
|
||||
"reference": server.reference,
|
||||
"username": server.ssh_username,
|
||||
"partner_name": server.partner_id.name if server.partner_id else False,
|
||||
"ipv4": server.ip_v4_address,
|
||||
"ipv6": server.ip_v6_address,
|
||||
"status": server.status,
|
||||
"os": server.os_id.name if server.os_id else False,
|
||||
"url": server.url,
|
||||
}
|
||||
return values
|
||||
|
||||
def _parse_system_variable_tools(self):
|
||||
"""Parser system variable of `tools` type.
|
||||
|
||||
Returns:
|
||||
dict(): `server` values of the `tower` variable.
|
||||
"""
|
||||
today = fields.Date.to_string(fields.Date.today())
|
||||
now = fields.Datetime.to_string(fields.Datetime.now())
|
||||
values = {
|
||||
"uuid": uuid.uuid4(),
|
||||
"today": today,
|
||||
"now": now,
|
||||
"today_underscore": re.sub(r"[-: .\/]", "_", today),
|
||||
"now_underscore": re.sub(r"[-: .\/]", "_", now),
|
||||
}
|
||||
return values
|
||||
|
||||
def _compose_variable_global_values_domain(self, variable_references):
|
||||
"""Compose domain for global variables
|
||||
Args:
|
||||
variable_references (list of Char): variable names
|
||||
|
||||
Returns:
|
||||
domain
|
||||
"""
|
||||
domain = [
|
||||
("is_global", "=", True),
|
||||
("variable_reference", "in", variable_references),
|
||||
]
|
||||
return domain
|
||||
|
||||
def _render_variable_values(self, variable_values):
|
||||
"""Renders variable values using other variable values.
|
||||
For example we have the following values:
|
||||
"server_root": "/opt/server"
|
||||
"server_assets": "{{ server_root }}/assets"
|
||||
|
||||
This function will render the "server_assets" variable:
|
||||
"server_assets": "/opt/server/assets"
|
||||
|
||||
Args:
|
||||
variable_values (dict): values to complete
|
||||
"""
|
||||
self.ensure_one()
|
||||
TemplateMixin = self.env["cx.tower.template.mixin"]
|
||||
for key, var_value in variable_values.items():
|
||||
# Render only if template is found
|
||||
if var_value and "{{ " in var_value:
|
||||
# Get variables used in value
|
||||
value_vars = TemplateMixin.get_variables_from_code(var_value)
|
||||
|
||||
# Render variables used in value
|
||||
res = self.get_variable_values(value_vars, apply_modifiers=True)
|
||||
|
||||
# Render value using variables
|
||||
variable_values[key] = TemplateMixin.render_code_custom(
|
||||
var_value, **res[self.id]
|
||||
)
|
||||
|
||||
def _apply_modifiers(self, variable_values):
|
||||
"""Apply pre-defined Python expression to the dictionary
|
||||
of variable values.
|
||||
|
||||
Args:
|
||||
variable_values (dict): variable values
|
||||
{record_id: {variable_reference: value}}
|
||||
"""
|
||||
variable_obj = self.env["cx.tower.variable"]
|
||||
|
||||
for record_id, values in variable_values.items():
|
||||
for variable_reference, value in values.items():
|
||||
if not value:
|
||||
continue
|
||||
|
||||
# ORM should cache resolved variables
|
||||
variable = variable_obj.get_by_reference(variable_reference)
|
||||
|
||||
# Should never happen.. anyway
|
||||
if not variable:
|
||||
continue
|
||||
|
||||
# Skip if no expression to apply
|
||||
if not variable.applied_expression:
|
||||
continue
|
||||
|
||||
# Evaluate expression
|
||||
eval_context = variable_obj._get_eval_context(value)
|
||||
try:
|
||||
safe_eval(
|
||||
variable.applied_expression,
|
||||
eval_context,
|
||||
mode="exec",
|
||||
nocopy=True,
|
||||
)
|
||||
variable_values[record_id][variable_reference] = eval_context.get(
|
||||
"result"
|
||||
)
|
||||
except Exception as e:
|
||||
_logger.error(
|
||||
"Error evaluating applied expression for "
|
||||
"variable %s value %s: %s",
|
||||
variable.name,
|
||||
value,
|
||||
str(e),
|
||||
)
|
||||
|
||||
def _get_current_server(self):
|
||||
"""Get current server record.
|
||||
This is needed to render system variables properly.
|
||||
|
||||
Returns:
|
||||
cx.tower.server(): server record
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
if self._name == "cx.tower.server":
|
||||
server = self
|
||||
elif self._name == "cx.tower.variable.value" and self.server_id:
|
||||
server = self.server_id
|
||||
else:
|
||||
server = None
|
||||
return server
|
||||
Reference in New Issue
Block a user