Skip to content

Commit

Permalink
[FIX] cetmix_tower_server: Server creation
Browse files Browse the repository at this point in the history
- Fix incorrect creation of server variables when generating a server from a template.
- Enable the creation of servers from a template with either a full copy of variables or only the specified ones.
- Allow the creation of servers with specific variable options.
- Optimize search of existing variables and creation of new ones

Task: 4253
  • Loading branch information
GabbasovDinar committed Jan 15, 2025
1 parent 3bb89e8 commit 3a0d0a3
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 74 deletions.
164 changes: 110 additions & 54 deletions cetmix_tower_server/models/cx_tower_server_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.exceptions import UserError, ValidationError


class CxTowerServerTemplate(models.Model):
Expand Down Expand Up @@ -171,6 +171,10 @@ def create_server_from_template(self, template_reference, server_name, **kwargs)
`variable_reference`: `variable_value_char`
eg:
{'branch': 'prod', 'odoo_version': '16.0'}
with_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
Expand Down Expand Up @@ -199,6 +203,10 @@ def _create_new_server(self, name, **kwargs):
`variable_reference`: `variable_value_char`
eg:
{'branch': 'prod', 'odoo_version': '16.0'}
with_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
Expand Down Expand Up @@ -251,7 +259,7 @@ def _get_fields_tower_server(self):
"server_log_ids",
]

def _prepare_server_values(self, **kwargs):
def _prepare_server_values(self, with_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,
Expand All @@ -260,6 +268,10 @@ def _prepare_server_values(self, **kwargs):
data.
Args:
with_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:
Expand Down Expand Up @@ -299,76 +311,120 @@ def _prepare_server_values(self, **kwargs):

values[field] = new_records

# custom specific variable values
# Handle configuration variables if provided.
configuration_variables = kwargs.pop("configuration_variables", None)
line_ids_variables = kwargs.pop("line_ids_variables", None)
configuration_variable_options = kwargs.pop(
"configuration_variable_options", {}
)

if configuration_variables:
# Validate required variables
self._validate_required_variables(configuration_variables)

variable_vals_list = []
# 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:
raise UserError(
_(
"Option with references '%(references)s' "
"is not found for variables.",
references=", ".join(missing_options),
)
)

# 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
}

for (
variable_reference,
variable_value,
) in configuration_variables.items():
variable = variable_obj.search(
[("reference", "=", variable_reference)]
server_variable_vals_list = []
for variable, variable_value in configuration_variable_dict.items():
variable_option = configuration_variable_options_dict.get(
variable.id
)
if not variable:
variable = variable_obj.create(
{
"name": variable_reference,
}
)
variable_option_id = variable_id = False

if not variable_value and line_ids_variables:
val_found = next(
(
v
for v in line_ids_variables.values()
if v.get("variable_reference") == variable_reference
),
None,
)
if val_found:
variable_value = val_found.get("value_char")
variable_option_id = val_found.get("option_id", False)
variable_id = val_found.get("variable_id", False)

variable_vals_list.append(
server_variable_vals_list.append(
(
0,
0,
{
"variable_id": variable.id or variable_id,
"value_char": variable_value or "",
"option_id": variable_option_id,
"variable_id": variable.id,
"value_char": variable_option
and variable_option.value_char
or variable_value,
"option_id": variable_option and variable_option.id,
},
)
)

# 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]
}
if with_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,
}
)

for new_command in 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
# 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
Expand Down
13 changes: 9 additions & 4 deletions cetmix_tower_server/readme/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ This function takes the following arguments:
'variable_reference': 'variable_value_char'
eg:
{'branch': 'prod', 'odoo_version': '16.0'}
- configuration_variable_options (Dict, optional): Custom configuration variable options.
Following format is used:
'variable_reference': 'option_reference'
eg:
{'language': 'language_variable_option_1'}
```

Here is a short example of an Odoo automated action that creates a new server when a Sales Order is confirmed:
Expand All @@ -33,7 +38,7 @@ Here is a short example of an Odoo automated action that creates a new server wh

```python
for record in records:

# Check confirmed orders
if record.state == "sale":
params = {
Expand All @@ -46,14 +51,14 @@ for record in records:
"odoo_version": "16.0"
},
}
# Create a new server from template with the 'demo_template' reference

# Create a new server from template with the 'demo_template' reference
env["cetmix.tower"].server_create_from_template(
template_reference="demo_template",
server_name=record.name,
**params
)

```

## Run a Command
Expand Down
83 changes: 81 additions & 2 deletions cetmix_tower_server/tests/test_server_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ def test_create_server_from_template_action(self):
"value_char": "test template url",
}
)
# add variable option
variable_url_option = self.VariableOption.create(
{
"name": "localhost",
"value_char": "localhost",
"variable_id": self.variable_url.id,
}
)

# create new server with new variable
self.ServerTemplate.create_server_from_template(
Expand All @@ -165,6 +173,9 @@ def test_create_server_from_template_action(self):
self.variable_version.reference: "test server version",
"new_variable": "new_value",
},
configuration_variable_options={
self.variable_url.reference: variable_url_option.reference,
},
)
new_server = self.Server.search([("name", "=", name)])

Expand All @@ -189,8 +200,8 @@ def test_create_server_from_template_action(self):
)
self.assertEqual(
var_url_value.value_char,
"test template url",
"Url variable values should be same as in the template",
variable_url_option.value_char,
"Url variable values should be same as option value",
)

var_new_value = new_server.variable_value_ids.filtered(
Expand Down Expand Up @@ -798,3 +809,71 @@ def test_server_template_variable_values_manager_access_rights(self):
self.server_template_sample.message_unsubscribe([self.user_bob.partner_id.id])
with self.assertRaises(AccessError):
other_own_variable_value.unlink()

def test_with_partial_removed_variables_from_wizard(self):
"""
Test that server creation only with specified
variables from wizard and option
"""
# create new variable option
option = self.VariableOption.create(
{
"name": "test",
"value_char": "test",
"variable_id": self.variable_dir.id,
}
)

# template with variables
self.server_template_sample.variable_value_ids = [
(
0,
0,
{
"variable_id": self.variable_path.id,
"value_char": "/var/log",
"required": False,
},
),
(
0,
0,
{
"variable_id": self.variable_dir.id,
"option_id": option.id,
"required": False,
},
),
]

action = self.server_template_sample.action_create_server()

# Open the wizard and fill in the data
wizard = (
self.env["cx.tower.server.template.create.wizard"]
.with_context(**action["context"])
.create(
{
"name": "Server from Template",
"ip_v4_address": "localhost",
"server_template_id": self.server_template_sample.id,
}
)
)

# Fill in the value for the required variable
with Form(wizard) as wizard_form:
wizard_form.line_ids.remove(0)
wizard_form.save()

wizard.action_confirm()

server = self.server_template_sample.server_ids
self.assertEqual(
len(server.variable_value_ids), 1, "Server variable must be 1!"
)
self.assertEqual(
server.variable_value_ids.value_char,
option.value_char,
"The variable value must be equal to the value from the option",
)
Loading

0 comments on commit 3a0d0a3

Please sign in to comment.