6 Commits

Author SHA1 Message Date
Vladislav Nesterov
ef4f57a1bd 13.0.2 2026-09-25 22:25:56 +07:00
cd6a835227 13.0.1 2026-09-18 13:32:29 +03:00
1c0e5cfdb3 13.0.0 2026-09-18 12:19:28 +03:00
b2cb33e9a8 12.0.3 2026-08-11 11:52:11 +03:00
d5ce634491 12.0.2 2026-07-22 13:27:40 +03:00
9d78742a10 12.0.1 2026-06-05 14:27:07 +03:00
31 changed files with 1531 additions and 916 deletions

View File

@@ -1,174 +1,33 @@
# Список изменений в версии 12.0.0
# Список изменений в версии 13.0.2
## Изменено
## Добавлено
### Глобально
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1164 | Добавлен модуль `decort_sdn_hypervisor_list`, позволяющий получить список гипервизоров SDN.|
| BANS-1162 | Добавлен модуль `decort_sdn_access_group_list`, позволяющий получить список групп доступа SDN.|
| BANS-1161 | Добавлен модуль `decort_sdn_segment_list`, позволяющий получить список доступных сегментов SDN. |
| BANS-1154 | Добавлен модуль `decort_sdn_segment`, позволяющий привести к целевому состоянию и получить информацию о сегментах SDN. |
| BANS-1158 | Добавлен модуль `decort_sdn_hypervisor`, позволяющий привести к целевому состоянию и получить информацию о гипервизоре SDN. |
| BANS-1156 | Добавлен модуль `decort_sdn_access_group`, позволяющий создать, привести к целевому состоянию и получить информацию о группе доступа SDN. |
| BANS-1163 | Добавлен модуль `decort_sdn_logical_port_list`, позволяющий получить список доступных логических портов SDN. |
| BANS-1159 | Добавлен модуль `decort_sdn_network_object_group`, позволяющий привести к целевому состоянию и получить информацию о группах сетевых объектов. |
| BANS-1157 | Добавлен модуль `decort_sdn_logical_port`, позволяющий привести к целевому состоянию и получить информацию о логическом порте. |
| BANS-1184 | Добавлен модуль `decort_sdn_logical_port_address`, позволяющий привести к целевому состоянию и получить информацию об адресе логического порта SDN. |
| BANS-1186 | Добавлен модуль `decort_sdn_network_object_group_logical_port`, позволяющий привести к целевому состоянию логические порты сетевых групп объектов SDN. |
| BANS-1185 | Добавлен модуль `decort_sdn_network_object_group_ip_range`, позволяющий привести к целевому состоянию и получить информацию о диапазоне IP-адресов в группе сетевых объектов SDN. |
| BANS-1193 | Добавлен модуль `decort_sdn_network_object_group_mac`, позволяющий привести к целевому состоянию и получить информацию о MAC-адресе в группе сетевых объектов SDN. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1011 | Добавлены возвращаемые значения `account_name`, `acl`, `created_by`, `created_datetime`, `created_timestamp`, `deleted_by`, `deleted_datetime`, `deleted_timestamp`, `description`, `destruction_datetime`, `destruction_timestamp`, `device_name`, `image_id`, `image_ids`, `milestones`, `params`, `parent_id`, `present_to`, `purge_datetime`, `purge_timestamp`, `replication`, `res_id`, `res_name`, `role`, `sep_type`, `shared`, `snapshots`, `tech_status`, `updated_by`, `updated_datetime`, `updated_timestamp`. |
| BANS-1104 | Добавлено возвращаемое значение `provision`. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-238 | Добавлена возможность изменить описание объекта. |
| BANS-1134 | Добавлены возвращаемые значения `account_name`, `acl`, `acl.account`, `acl.k8s`, `acl.rg`, `bservice_id`, `created_by`, `created_datetime`, `created_timestamp`, `deleted_by`, `deleted_datetime`, `deleted_timestamp`, `extnet_only`, `k8ci_id`, `k8ci_name`, `lb_ha_ips`, `lb_ha_ips.backend`, `lb_ha_ips.frontend`, `lb_ha_mode`, `network_plugin`, `node_groups`, `node_groups.master`, `node_groups.master.id`, `node_groups.master.name`, `node_groups.master.annotations`, `node_groups.master.labels`, `node_groups.master.taints`, `node_groups.master.vms.ext_ip`, `node_groups.master.vms.name`, `node_groups.master.vms.status`, `node_groups.worker`, `node_groups.worker.id`, `node_groups.worker.name`, `node_groups.worker.annotations`, `node_groups.worker.labels`, `node_groups.worker.taints`, `node_groups.worker.vms.ext_ip`, `node_groups.worker.vms.name`, `node_groups.worker.vms.status`, `rg_name`, `updated_by`, `updated_datetime`, `updated_timestamp`, `with_lb`. |
### Модуль decort_rg
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1063 | Добавлены возвращаемые значения `account_name`, `acl`, `cpu_allocation_parameter`, `cpu_allocation_ratio`, `created_by`, `created_timestamp`, `created_datetime`, `deleted_by`, `deleted_timestamp`, `deleted_datetime`, `dirty`, `guid`, `lock_status`, `milestones`, `secret`, `updated_by`, `updated_timestamp`, `updated_datetime`, `vm_features`. |
### Модуль decort_vins
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1061 | Добавлены возвращаемые значения `account_name`, `created_by`, `created_datetime`, `created_timestamp`, `default_gw`, `default_qos`, `deleted_by`, `deleted_datetime`, `deleted_timestamp`, `description`, `guid`, `lock_status`, `manager_id`, `manager_type`, `milestones`, `net_prefix`, `pre_reservation_count`, `redundant`, `rg_name`, `secondary_vnfdev_id`, `security_group_mode`, `updated_by`, `updated_datetime`, `updated_timestamp`, `user_managed`, `vms`, `vnfdev`, `vnfs`, `vxlan_id`. |
| BANS-1132 | Добавлен параметр `security_group_mode`. |
| BANS-1463 | Добавлен модуль `decort_vm_folder_list`, позволяющий получить список папок для группировки ВМ в ресурсной группе. |
| BANS-1467 | Добавлен модуль `decort_vm_folder_access`, позволяющий управлять правами доступа к папке виртуальных машин. |
| BANS-1464 | Добавлен модуль `decort_vm_folder`, позволяющий привести к целевому состоянию и получить информацию о папке для группировки ВМ в ресурсной группе. |
### Модуль decort_vm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1088 | Добавлено возвращаемое значение `weight`. |
### Модуль decort_disk_list
| BANS-1466 | Добавлены параметр и возвращаемое значение `folder_id`. |
### Модуль decort_vm_list
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1083 | Добавлен параметр `filter.vm_id`. |
| BANS-1084 | Добавлен параметр `filter.rg_id`. |
| BANS-1096 | Добавлено возвращаемое значение `independent`. |
| BANS-1103 | Добавлено возвращаемое значение `provision`. |
| BANS-1468 | Добавлено возвращаемое значение `folder_id`. |
| BANS-1476 | Добавлен параметр `include_descendants`. |
| BANS-1477 | Добавлен параметр `filter.folder_id` для фильтрации виртуальных машин по идентификатору папки для группировки ВМ в ресурсной группе. |
### Модуль decort_image
### Модуль decort_audit_list
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1094 | Добавлено возвращаемое значение `independent`. |
| BANS-1108 | Добавлено возвращаемое значение `links_to`. |
### Модуль decort_zone_list
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1116 | Добавлено возвращаемое значение `drs_dx_app_id`. |
| BANS-1117 | Добавлено возвращаемое значение `drs_dx_url`. |
| BANS-1118 | Добавлено возвращаемое значение `drs`. |
| BANS-1119 | Добавлено возвращаемое значение `drs_name`. |
| BANS-1120 | Добавлено возвращаемое значение `drs_uid`. |
| BANS-1121 | Добавлено возвращаемое значение `drs_dx_sso_url`. |
| BANS-1147 | Добавлено возвращаемое значение `drs_dx_ssl_skip_verify`. |
| BANS-1148 | Добавлено возвращаемое значение `drs_bvs_domain`. |
| BANS-1149 | Добавлено возвращаемое значение `drs_broadcast_ip_addr`. |
| BANS-1166 | Добавлено возвращаемое значение `drs_dx_sso_type`. |
| BANS-1150 | Добавлено возвращаемое значение `drs_ping_ip_addr`. |
### Модуль decort_zone
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1124 | Добавлено возвращаемое значение `drs_dx_app_id`. |
| BANS-1122 | Добавлено возвращаемое значение `drs`. |
| BANS-1125 | Добавлено возвращаемое значение `drs_dx_url`. |
| BANS-1123 | Добавлено возвращаемое значение `drs_uid`. |
| BANS-1126 | Добавлено возвращаемое значение `drs_name`. |
| BANS-1127 | Добавлено возвращаемое значение `drs_dx_sso_url`. |
| BANS-1143 | Добавлено возвращаемое значение `drs_dx_ssl_skip_verify`. |
| BANS-1144 | Добавлено возвращаемое значение `drs_bvs_domain`. |
| BANS-1145 | Добавлено возвращаемое значение `drs_broadcast_ip_addr`. |
| BANS-1146 | Добавлено возвращаемое значение `drs_ping_ip_addr`. |
| BANS-1165 | Добавлено возвращаемое значение `drs_dx_sso_type`. |
### Модуль decort_lb
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1112 | Добавлены возвращаемые значения `acl`, `backend_ha_ip_addr`, `created_by`, `created_datetime`, `created_timestamp`, `deleted_by`, `deleted_datetime`, `deleted_timestamp`, `description`, `dp_api_user`, `ext_net_id`, `frontend_ha_ip_addr`, `guid`, `ha_mode`, `manager_id`, `manager_type`, `milestones`, `part_of_k8s`, `primary_node.backend_ip_addr`, `primary_node.frontend_ip_addr`, `primary_node.guid`, `primary_node.mgmt_ip`, `primary_node.net_id`, `primary_node.vm_id`, `rg_name`, `secondary_node.backend_ip_addr`, `secondary_node.frontend_ip_addr`, `secondary_node.guid`, `secondary_node.mgmt_ip`, `secondary_node.net_id`, `secondary_node.vm_id`, `updated_by`, `updated_datetime`, `updated_timestamp`, `user_managed`, `vins_id`. |
### Модуль decort_user
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1176 | Добавлены возвращаемые значения `consumed.storage_policies.storage_size_quota_gb`, `reserved.storage_policies.storage_size_quota_gb`. |
| BANS-1840 | Добавлено возвращаемое значение `vmfolder_id`. |
## Удалено
### Глобально
| Идентификатор<br>задачи | Описание |
| --- | --- |
### Модуль decort_account
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1040 | Удалены возвращаемые значения `computes_amount` , `createdTime`, `createdTime_readable`, `deactivationTime`, `deactivationTime_readable`, `deletedTime`, `deletedTime_readable`, `resourceLimits`, `resourceLimits.CU_C`, `resourceLimits.CU_D`, `resourceLimits.CU_DM`, `resourceLimits.CU_I`, `resourceLimits.CU_M`, `resourceLimits.gpu_units`, `updatedTime`, `updatedTime_readable`, `vinses_amount` в связи с переименованием в `vm_counts` , `created_timestamp`, `created_datetime`, `deactivation_timestamp`, `deactivation_datetime`, `deleted_timestamp`, `deleted_datetime`, `quotas`, `quotas.cpu_count`, `quotas.disk_size_gb`, `quotas.storage_size_gb`, `quotas.ext_ip_count`, `quotas.ram_size_mb`, `quotas.gpu_count`, `updated_timestamp`, `updated_datetime`, `vins_count`. |
| BANS-1067 | Удалены параметры `access_emails`, `quotas.cpu`, `quotas.disk_size`, `quotas.gpu`, `quotas.public_ip`, `quotas.ram` в связи с переименованием в `send_access_emails`, `quotas.cpu_count`, `quotas.storage_size_gb`, `quotas.gpu_count`, `quotas.ext_ip_count`, `quotas.ram_size_mb`. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1011 | Удалены возвращаемые значения `computes`, `gid`, `iotune`, `pool`, `size`, `size_available`, `size_used`, `state` в связи с переименованием в `vms`, `grid_id`, `io_tune`, `sep_pool_name`, `size_max_gb`, `size_available_gb`, `size_used_gb`, `status` |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1068 | Для параметра `description` удалено значение по умолчанию. |
| BANS-1134 | Удалены возвращаемые значения `techStatus`, `state`, `k8s_Masters`, `k8s_Masters.cpu`, `k8s_Masters.ram`, `k8s_Masters.disk`, `k8s_Masters.num`, `k8s_Masters.detailedInfo`, `k8s_Masters.detailedInfo.techStatus`, `k8s_Workers`, `k8s_Workers.cpu`, `k8s_Workers.ram`, `k8s_Workers.disk`, `k8s_Workers.num`, `k8s_Workers.detailedInfo`, `k8s_Workers.detailedInfo.techStatus` в связи с переименованием в `tech_status`, `status`, `node_groups.master`, `node_groups.master.node_cpu_count`, `node_groups.master.node_ram_size_mb`, `node_groups.master.node_boot_disk_size_gb`, `node_groups.master.node_count`, `node_groups.master.vms`, `node_groups.master.vms.tech_status`, `node_groups.worker`, `node_groups.worker.node_cpu_count`, `node_groups.worker.node_ram_size_mb`, `node_groups.worker.node_boot_disk_size_gb`, `node_groups.worker.node_count`, `node_groups.worker.vms`, `node_groups.worker.vms.tech_status`. |
### Модуль decort_rg
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1063 | Удалены возвращаемые значения `computes`, `defNetId`, `defNetType`, `gid`, `quota`, `resTypes`, `uniqPools`, `ViNS` в связи с переименованием в `vm_ids`, `default_net_id`, `default_net_type`, `grid_id`, `quotas`, `resource_types`, `sep_pools`, `vins_ids` |
### Модуль decort_vins
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1061 | Удалены возвращаемые значения `int_net_addr`, `gid`, `state` в связи с переименованием в `net_ip`, `grid_id`, `status`. |
| BANS-1061 | Удалены возвращаемые значения `custom_net_addr`, `ext_ip_addr`, `ssh_ipaddr`, `ssh_password`, `ssh_port`. |
| BANS-1054 | Для параметра `state` значение по умолчанию изменено на значение по умолчанию если объект не существует или безвозвратно удалён. |
### Модуль decort_disk_list
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1085 | Удалён параметр `filter.type`. |
| BANS-1086 | Удалено возвращаемое значение `type`. |
### Модуль decort_user
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1080 | Удалено возвращаемое значение `emailaddresses` в связи с переименованием в `email_addresses`. |
| BANS-1176 | Удалены возвращаемые значения `resource_consumed`, `resource_consumed.cpu`, `resource_consumed.disksize`, `resource_consumed.disksizemax`, `resource_consumed.extips`, `resource_consumed.gpu`, `resource_consumed.ram`, `resource_consumed.policies`, `resource_consumed.policies.disksize`, `resource_consumed.policies.disksizemax`, `resource_consumed.policies.seps.disksize`, `resource_consumed.policies.seps.disksizemax`, `resource_consumed.seps`, `resource_consumed.seps.disksize`, `resource_consumed.seps.disksizemax`, `resource_reserved`, `resource_reserved.cpu`, `resource_reserved.disksize`, `resource_reserved.disksizemax`, `resource_reserved.extips`, `resource_reserved.gpu`, `resource_reserved.ram`, `resource_reserved.policies`, `resource_reserved.policies.disksize`, `resource_reserved.policies.disksizemax`, `resource_reserved.policies.seps.disksize`, `resource_reserved.policies.seps.disksizemax`, `resource_reserved.seps`, `resource_reserved.seps.disksize`, `resource_reserved.seps.disksizemax` в связи с переименованием в `consumed`, `consumed.cpu_count`, `consumed.storage_size_gb_by_real_usage`, `consumed.storage_size_gb_by_disk_max`, `consumed.ext_ip_count`, `consumed.gpu_count`, `consumed.ram_size_mb`, `consumed.storage_policies`, `consumed.storage_policies.storage_size_gb_by_real_usage`, `consumed.storage_policies.storage_size_gb_by_disk_max`, `consumed.storage_policies.sep_pools.storage_size_gb_by_real_usage`, `consumed.storage_policies.sep_pools.storage_size_gb_by_disk_max`, `consumed.sep_pools`, `consumed.sep_pools.storage_size_gb_by_real_usage`, `consumed.sep_pools.storage_size_gb_by_disk_max`, `reserved`, `reserved.cpu_count`, `reserved.storage_size_gb_by_real_usage`, `reserved.storage_size_gb_by_disk_max`, `reserved.ext_ip_count`, `reserved.gpu_count`, `reserved.ram_size_mb`, `reserved.storage_policies`, `reserved.storage_policies.storage_size_gb_by_real_usage`, `reserved.storage_policies.storage_size_gb_by_disk_max`, `reserved.storage_policies.sep_pools.storage_size_gb_by_real_usage`, `reserved.storage_policies.sep_pools.storage_size_gb_by_disk_max`, `reserved.sep_pools`, `reserved.sep_pools.storage_size_gb_by_real_usage`, `reserved.sep_pools.storage_size_gb_by_disk_max`. |
### Модуль decort_lb
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1112 | Удалены возвращаемые значения `backends.serverDefaultSettings`, `backends.servers.address`, `backends.servers.serverSettings`, `frontends.backend`, `frontends.bindings.address`, `gid`, `state`, `sysctl` в связи с переименованием в `backends.server_default_settings`, `backends.servers.ip_addr`, `backends.servers.server_settings`, `frontends.backend_name`, `frontends.bindings.ip_addr`, `grid_id`, `status`, `sysctl_params`. |
## Исправлено
### Модуль decort_account
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1055 | При передаче несуществующего ID аккаунта модуль завершал свою работу с некорректным текстом ошибки. Также модуль повторно выполнял запрос к API `cloudapi/account/get` без необходимости. |
### Модуль decort_vins
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1053 | Модуль завершал свою работу ошибкой Python при выполнении восстановления внутренней сети из корзины. |
| BANS-1079 | Модуль отключал внешнюю сеть при незаданном параметре `ext_net_id`. |
| BANS-937 | Модуль завершал работу ошибкой запроса к API при `state: absent` для несуществующего объекта. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1062 | Модуль завершал работу ошибкой при `state: absent` для несуществующего объекта. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1188 | Модуль завершал работу ошибкой при `state: absent` для объекта, который не найден или безвозвратно удален. |

View File

@@ -5,6 +5,7 @@
| Версия платформы | Версия модулей Ansible |
|:----------------:|:--------------------------:|
| 4.7.0 | 13.0.x |
| 4.6.0 | 12.0.x |
| 4.5.0 | 11.0.x |
| 4.4.0 | 10.0.x |

View File

@@ -56,7 +56,7 @@ class DecortAccountList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -90,7 +90,7 @@ class DecortAuditList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -19,13 +19,12 @@ class decort_bservice(DecortController):
validated_acc_id = 0
validated_rg_id = 0
self.bservice_info = None
self.is_bservice_stopped_or_will_be_stopped: None | bool = None
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] == 0:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = "Cannot manage Basic Services when its ID is 0 and name is empty."
self.fail_json(**self.result)
self.amodule.fail_json(**self.result)
if not arg_amodule.params['id']:
if not arg_amodule.params['rg_id']: # RG ID is not set -> locate RG by name -> need account ID
validated_acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'],
@@ -36,7 +35,7 @@ class decort_bservice(DecortController):
self.result['msg'] = ("Current user does not have access to the account ID {} / "
"name '{}' or non-existent account specified.").format(arg_amodule.params['account_id'],
arg_amodule.params['account_name'])
self.fail_json(**self.result)
self.amodule.fail_json(**self.result)
# fail the module -> exit
# now validate RG
validated_rg_id, validated_rg_model = self.rg_find(
@@ -55,14 +54,27 @@ class decort_bservice(DecortController):
arg_amodule.params['rg_name'] = validated_rg_model.name
validated_acc_id = validated_rg_model.account_id
self.bservice_id, self.bservice_info = self.bservice_find(
validated_acc_id,
validated_rg_id,
arg_amodule.params['name'],
arg_amodule.params['id']
self.bservice_id, self._bservice_info = self.bservice_find(
account_id=validated_acc_id,
rg_id=validated_rg_id,
bservice_name=arg_amodule.params['name'],
bservice_id=arg_amodule.params['id'],
fail_if_not_found=False,
)
self.acc_id = validated_acc_id or self.bservice_info['accountId']
if arg_amodule.params['id'] and self.bservice_id == 0:
if arg_amodule.params['state'] == 'absent':
self.exit()
else:
self.message(
self.MESSAGES.obj_not_found(
obj='bservice',
id=arg_amodule.params['id'],
)
)
self.exit(fail=True)
self.acc_id = validated_acc_id or self.bservice_info['account_id']
if self.bservice_id and self.bservice_info['status'] != 'DESTROYED':
self.bservice_should_exist = True
@@ -92,8 +104,8 @@ class decort_bservice(DecortController):
self.result['changed'] = False
if self.bservice_id:
self.result['msg'] = ("Invalid target state '{}' requested for B-service ID {} in the "
"current status '{}'.").format(self.bservice_id,
self.amodule.params['state'],
"current status '{}'.").format(self.amodule.params['state'],
self.bservice_id,
self.bservice_info['status'])
else:
self.result['msg'] = ("Invalid target state '{}' requested for non-existent B-service name '{}' "
@@ -112,7 +124,7 @@ class decort_bservice(DecortController):
zone_id=self.aparams['zone_id'],
)
if self.bservice_id:
_, self.bservice_info = self.bservice_get_by_id(self.bservice_id)
self._bservice_info = self.bservice_get_by_id(self.bservice_id)
self.bservice_state(self.bservice_info, self.aparams['state'])
self.bservice_should_exist = True
@@ -122,9 +134,9 @@ class decort_bservice(DecortController):
self.bservice_state(self.bservice_info,d_state)
aparam_zone_id = self.aparams['zone_id']
if aparam_zone_id is not None and aparam_zone_id != self.bservice_info['zoneId']:
self.bservice_migrate_to_zone(
bs_id=self.bservice_id,
if aparam_zone_id is not None and aparam_zone_id != self.bservice_info['zone_id']:
self.sdk_checkmode(self.api.cloudapi.bservice.migrate_to_zone)(
bservice_id=self.bservice_id,
zone_id=aparam_zone_id,
)
return
@@ -155,18 +167,9 @@ class decort_bservice(DecortController):
)
if check_mode:
# in check mode return immediately with the default values
return ret_dict
ret_dict['id'] = self.bservice_info['id']
ret_dict['name'] = self.bservice_info['name']
ret_dict['techStatus'] = self.bservice_info['techStatus']
ret_dict['state'] = self.bservice_info['status']
ret_dict['rg_id'] = self.bservice_info['rgId']
ret_dict['account_id'] = self.bservice_info['accountId']
ret_dict['groups'] = self.bservice_info['groups']
ret_dict['zone_id'] = self.bservice_info['zoneId']
return ret_dict
return self.bservice_info
@property
def amodule_init_args(self) -> dict:
@@ -228,14 +231,14 @@ class decort_bservice(DecortController):
self.is_bservice_stopped_or_will_be_stopped = (
(
self.bservice_info['techStatus'] == 'STOPPED'
self.bservice_info['tech_status'] == 'STOPPED'
and (
self.aparams['state'] is None
or self.aparams['state'] in ('present', 'stopped')
)
)
or (
self.bservice_info['techStatus'] != 'STOPPED'
self.bservice_info['tech_status'] != 'STOPPED'
and self.aparams['state'] == 'stopped'
)
)
@@ -244,7 +247,7 @@ class decort_bservice(DecortController):
check_errors = True
if (
self.aparams['zone_id'] is not None
and self.aparams['zone_id'] != self.bservice_info['zoneId']
and self.aparams['zone_id'] != self.bservice_info['zone_id']
and not self.is_bservice_stopped_or_will_be_stopped
):
check_errors = True
@@ -270,7 +273,10 @@ class decort_bservice(DecortController):
if self.amodule.check_mode:
self.result['changed'] = False
if self.bservice_id:
if (
self.bservice_id
or (self.aparams['rg_id'] and self.aparams['name'])
):
self.result['failed'] = False
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
@@ -309,10 +315,12 @@ class decort_bservice(DecortController):
else:
self.action(amodule.params['state'])
elif self.bservice_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'):
if amodule.params['state'] == 'present':
self.create()
self.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
elif amodule.params['state'] in ['enabled', 'disabled']:
self.error()
elif amodule.params['state'] == 'absent':
self.nop()
else:
state = amodule.params['state']
@@ -329,7 +337,9 @@ class decort_bservice(DecortController):
amodule.fail_json(**self.result)
else:
if self.bservice_should_exist:
_, self.bservice_info = self.bservice_get_by_id(self.bservice_id)
self._bservice_info = self.bservice_get_by_id(
self.bservice_id
)
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:

View File

@@ -63,7 +63,7 @@ class decort_disk(DecortController):
if arg_amodule.params['place_with']:
image_id, image_facts = self.image_find(arg_amodule.params['place_with'], "", 0)
arg_amodule.params['sep_id'] = image_facts['sepId']
arg_amodule.params['sep_id'] = image_facts['sep_id']
self.disk_id = validated_disk_id
self.disk_info = validated_disk_facts

View File

@@ -77,7 +77,7 @@ class DecortDiskList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -75,7 +75,7 @@ class DecortFlipGroupList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -21,7 +21,12 @@ class decort_group(DecortController):
validated_bservice_id = None
#find and validate B-Service
validated_bservice_id, bservice_info = self.bservice_get_by_id(arg_amodule.params['bservice_id'])
validated_bservice_id, self._bservice_info = self.bservice_find(
account_id=None,
rg_id=None,
bservice_id=arg_amodule.params['bservice_id'],
fail_if_not_found=False,
)
if not validated_bservice_id:
self.result['failed'] = True
self.result['changed'] = False
@@ -29,15 +34,14 @@ class decort_group(DecortController):
self.amodule.fail_json(**self.result)
#find group
self.bservice_id = validated_bservice_id
self.bservice_info = bservice_info
self.group_id,self.group_info = self.group_find(
bs_id=validated_bservice_id,
bs_info=bservice_info,
bs_info=self.bservice_info,
group_id=arg_amodule.params['id'],
group_name=arg_amodule.params['name'],
)
self.acc_id = self.bservice_info['accountId']
self.rg_id = self.bservice_info['rgId']
self.acc_id = self.bservice_info['account_id']
self.rg_id = self.bservice_info['rg_id']
if self.group_id:
self.group_should_exist = True
@@ -88,17 +92,19 @@ class decort_group(DecortController):
warning=True,
)
self.group_id=self.group_provision(
bs_id=self.bservice_id,
arg_name=self.amodule.params['name'],
arg_count=self.amodule.params['count'],
arg_cpu=self.amodule.params['cpu'],
arg_ram=self.amodule.params['ram'],
arg_boot_disk=self.amodule.params['boot_disk'],
arg_image_id=self.amodule.params['image_id'],
arg_role=self.amodule.params['role'],
arg_network=self.amodule.params['networks'],
arg_timeout=self.amodule.params['timeoutStart'],
aparam_networks = self.amodule.params['networks'] or []
self.group_id = self.sdk_checkmode(self.api.cloudapi.bservice.group_add)(
bservice_id=self.bservice_id,
name=self.amodule.params['name'],
vm_count=self.amodule.params['count'],
cpu_count=self.amodule.params['cpu'],
ram_size_mb=self.amodule.params['ram'],
boot_disk_size_gb=self.amodule.params['boot_disk'],
image_id=self.amodule.params['image_id'],
role=self.amodule.params['role'],
vins_ids=[n['id'] for n in aparam_networks if n['type'] == 'VINS'],
extnet_ids=[n['id'] for n in aparam_networks if n['type'] == 'EXTNET'],
start_timeout=self.amodule.params['timeoutStart'],
chipset=chipset,
storage_policy_id=self.aparams['storage_policy_id'],
)
@@ -146,14 +152,15 @@ class decort_group(DecortController):
aparam_value = self.aparams[aparam_name]
group_info_value = self.group_info[info_key]
if aparam_value != None and aparam_value != group_info_value:
self.group_update(
bs_id=self.bservice_id,
gr_dict=self.group_info,
arg_cpu=self.aparams['cpu'],
arg_disk=self.aparams['boot_disk'],
arg_name=self.aparams['name'],
arg_role=self.aparams['role'],
arg_ram=self.aparams['ram'],
self.sdk_checkmode(self.api.cloudapi.bservice.group_update)(
bservice_id=self.bservice_id,
bsgroup_id=self.group_info['id'],
force=True,
cpu_count=self.aparams['cpu'],
boot_disk_size_gb=self.aparams['boot_disk'],
name=self.aparams['name'],
ram_size_mb=self.aparams['ram'],
role=self.aparams['role'],
)
break
@@ -166,9 +173,9 @@ class decort_group(DecortController):
def destroy(self):
self.group_delete(
self.bservice_id,
self.group_id
self.sdk_checkmode(self.api.cloudapi.bservice.group_remove)(
bservice_id=self.bservice_id,
bsgroup_id=self.group_id,
)
self.group_should_exist = False

View File

@@ -46,9 +46,9 @@ class decort_image(DecortController):
)
if self.virt_image_info:
_, linked_image_info = self._image_get_by_id(
image_id=self.virt_image_info['linkTo']
image_id=self.virt_image_info['link_to']
)
self.acc_id = linked_image_info['accountId']
self.acc_id = linked_image_info['account_id']
if (
self.aparams['virt_name'] is not None
and self.aparams['virt_name']
@@ -61,10 +61,10 @@ class decort_image(DecortController):
or self.aparams['image_name'] is not None
):
self.validated_image_id, self.image_info = (
self.decort_image_find(amodule)
self.decort_image_find(amodule, fail_if_not_found=False)
)
if self.image_info:
self.acc_id = self.image_info['accountId']
self.acc_id = self.image_info['account_id']
if (
amodule.params['image_name']
and amodule.params['image_name'] != self.image_info['name']
@@ -72,6 +72,23 @@ class decort_image(DecortController):
decort_image.decort_image_rename(self,amodule)
self.result['msg'] = ("Image renamed successfully")
if (
self.validated_image_id == 0
and self.aparams['image_id']
and not self.validated_virt_image_id
and not self.aparams['virt_name']
):
if self.aparams['state'] == 'absent':
self.exit()
else:
self.message(
self.MESSAGES.obj_not_found(
obj='image',
id=self.aparams['image_id'],
)
)
self.exit(fail=True)
if self.validated_image_id:
self.check_amodule_args_for_change()
elif self.validated_virt_image_id:
@@ -81,12 +98,13 @@ class decort_image(DecortController):
else:
self.check_amodule_args_for_create_image()
def decort_image_find(self, amodule):
def decort_image_find(self, amodule, fail_if_not_found=True):
# function that finds the OS image
image_id, image_facts = self.image_find(image_id=amodule.params['image_id'], image_name=self.validated_image_name,
account_id=self.validated_account_id, rg_id=0,
sepid=amodule.params['sep_id'],
pool=amodule.params['pool'])
pool=amodule.params['pool'],
fail_if_not_found=fail_if_not_found,)
return image_id, image_facts
def decort_virt_image_find(self, amodule):
@@ -150,28 +168,30 @@ class decort_image(DecortController):
)
# function that creates OS image
image_facts = self.image_create(
img_name=self.validated_image_name,
url=amodule.params['url'],
self.sdk_checkmode(self.api.ca.image.create)(
name=self.validated_image_name,
download_url=amodule.params['url'],
boot_mode=boot_mode,
boot_loader_type=loader_type,
hot_resize=hot_resize,
username=amodule.params['image_username'],
password=amodule.params['image_password'],
os_user_name=amodule.params['image_username'],
os_password=amodule.params['image_password'],
account_id=self.validated_account_id,
usernameDL=amodule.params['usernameDL'],
passwordDL=amodule.params['passwordDL'],
sepId=amodule.params['sepId'],
poolName=amodule.params['poolName'],
download_user_name=amodule.params['usernameDL'],
download_password=amodule.params['passwordDL'],
sep_id=amodule.params['sepId'],
sep_pool_name=amodule.params['poolName'],
network_interface_naming=network_interface_naming,
storage_policy_id=amodule.params['storage_policy_id'],
)
self.result['changed'] = True
return image_facts
return
def decort_virt_image_link(self,amodule):
# function that links an OS image to a virtual one
self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.target_image_id)
self.sdk_checkmode(self.api.ca.image.link)(
virtual_image_id=self.validated_virt_image_id,
target_image_id=self.target_image_id,
)
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.target_image_id,
@@ -189,9 +209,9 @@ class decort_image(DecortController):
def decort_virt_image_create(self,amodule):
# function that creates a virtual image
image_facts = self.virt_image_create(
self.sdk_checkmode(self.api.ca.image.create_virtual)(
name=amodule.params['virt_name'],
target_id=self.target_image_id,
target_image_id=self.target_image_id,
account_id=self.aparams['account_id'],
)
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
@@ -250,14 +270,14 @@ class decort_image(DecortController):
ret_dict['id'] = arg_image_facts['id']
ret_dict['name'] = arg_image_facts['name']
ret_dict['size'] = arg_image_facts['size']
ret_dict['size'] = arg_image_facts['size_gb']
# ret_dict['arch'] = arg_image_facts['architecture']
ret_dict['sep_id'] = arg_image_facts['sepId']
ret_dict['pool'] = arg_image_facts['pool']
ret_dict['sep_id'] = arg_image_facts['sep_id']
ret_dict['pool'] = arg_image_facts['sep_pool_name']
ret_dict['state'] = arg_image_facts['status']
ret_dict['linkto'] = arg_image_facts['linkTo']
ret_dict['accountId'] = arg_image_facts['accountId']
ret_dict['boot_mode'] = arg_image_facts['bootType']
ret_dict['linkto'] = arg_image_facts['link_to']
ret_dict['accountId'] = arg_image_facts['account_id']
ret_dict['boot_mode'] = arg_image_facts['boot_mode']
ret_dict['boot_loader_type'] = ''
match arg_image_facts['type']:
@@ -268,13 +288,13 @@ class decort_image(DecortController):
ret_dict['boot_loader_type'] = boot_loader_type
ret_dict['network_interface_naming'] = arg_image_facts[
'networkInterfaceNaming'
'network_interface_naming'
]
ret_dict['hot_resize'] = arg_image_facts['hotResize']
ret_dict['hot_resize'] = arg_image_facts['hot_resize']
ret_dict['storage_policy_id'] = arg_image_facts['storage_policy_id']
ret_dict['to_clean'] = arg_image_facts['to_clean']
ret_dict['independent'] = arg_image_facts['independent']
ret_dict['links_to'] = arg_image_facts['linksTo']
ret_dict['links_to'] = arg_image_facts['links_to']
return ret_dict
@property
@@ -502,7 +522,7 @@ class decort_image(DecortController):
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
self.image_change_storage_policy(
self.sdk_checkmode(self.api.ca.image.change_storage_policy)(
image_id=self.validated_virt_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
@@ -510,7 +530,7 @@ class decort_image(DecortController):
if amodule.params['state'] == "absent" and self.validated_virt_image_id:
amodule.image_id_delete = self.validated_virt_image_id
image_id, image_facts = self.decort_virt_image_find(amodule)
if image_facts['status'] != 'PURGED':
if image_facts['status'] != sdk_types.ImageStatus.PURGED:
self.decort_image_delete(amodule)
elif amodule.params['image_name'] or amodule.params['image_id']:
@@ -530,7 +550,10 @@ class decort_image(DecortController):
elif amodule.params['state'] == "absent" and self.validated_image_id:
amodule.image_id_delete = self.validated_image_id
image_id, image_facts = self.decort_image_find(amodule)
if image_facts['status'] != 'DESTROYED':
if image_facts['status'] not in [
sdk_types.ImageStatus.DESTROYED,
sdk_types.ImageStatus.PURGED,
]:
self.decort_image_delete(amodule)
if self.validated_image_id:
@@ -539,7 +562,7 @@ class decort_image(DecortController):
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
self.image_change_storage_policy(
self.sdk_checkmode(self.api.ca.image.change_storage_policy)(
image_id=self.validated_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)

View File

@@ -62,7 +62,8 @@ class DecortImageList(DecortController):
choices=sdk_types.ImageStatus._member_names_,
),
type=dict(
type='str',
type='list',
elements='str',
choices=sdk_types.ImageType._member_names_,
),
storage_policy_id=dict(
@@ -145,7 +146,7 @@ class DecortImageList(DecortController):
),
storage_policy_id=aparam_filter['storage_policy_id'],
type=(
sdk_types.ImageType[aparam_type]
[sdk_types.ImageType[type_] for type_ in aparam_type]
if aparam_type else None
),
page_number=aparam_pagination['number'],

View File

@@ -104,7 +104,9 @@ class decort_k8s(DecortController):
self.k8s_info['vins_id'] = self.k8s_vins_id
self.k8s_info['config'] = None
if self.amodule.params['getConfig'] and self.k8s_info['tech_status'] == "STARTED":
self.k8s_info['config'] = self.k8s_getConfig()
self.k8s_info['config'] = self.api.cloudapi.k8s.get_config(
k8s_id=self.k8s_id,
)
return self.k8s_info
def nop(self):
@@ -269,7 +271,7 @@ class decort_k8s(DecortController):
aparam_zone_id is not None
and aparam_zone_id != self.k8s_info['zone_id']
):
self.k8s_migrate_to_zone(
self.sdk_checkmode(self.api.cloudapi.k8s.migrate_to_zone)(
k8s_id=self.k8s_id,
zone_id=aparam_zone_id,
)

View File

@@ -184,7 +184,7 @@ class decort_lb(DecortController):
aparam_zone_id is not None
and aparam_zone_id != self.lb_info.zone_id
):
self.lb_migrate_to_zone(
self.sdk_checkmode(self.api.cloudapi.lb.migrate_to_zone)(
lb_id=self.lb_info.id,
zone_id=aparam_zone_id,
)

View File

@@ -234,6 +234,7 @@ class decort_rg(DecortController):
self.amodule.params['quotas'],
"", # this is location code. TODO: add module argument
sdn_access_group_id=self.aparams['sdn_access_group_id'],
enable_default_gateway=self.aparams['enable_default_gateway'],
)
if self.rg_id:
@@ -410,6 +411,9 @@ class decort_rg(DecortController):
sdn_access_group_id=dict(
type='str',
),
enable_default_gateway=dict(
type='bool',
),
),
supports_check_mode=True,
)
@@ -431,6 +435,66 @@ class decort_rg(DecortController):
)
check_errors = True
if self.aparams['enable_default_gateway'] is not None:
if (
(
self.aparams['def_netType'] is None
and self.rg_info.default_net_type != sdk_types.RGDefaultNetType.PRIVATE # noqa: E501
) or (
self.aparams['def_netType'] is not None
and self.aparams['def_netType'] != "PRIVATE"
)
):
self.message(
'Check for parameter "enable_default_gateway" failed: '
'cannot use param enable_default_gateway with a '
'non-private default network.'
)
check_errors = True
if self.rg_info.default_net_type == sdk_types.RGDefaultNetType.PRIVATE: # noqa: E501
def_net = self.api.cloudapi.vins.get(
vins_id=self.rg_info.default_net_id
)
if (
def_net.enable_default_gateway
!= self.aparams['enable_default_gateway']
):
self.message(
'Check for parameter "enable_default_gateway" '
'failed: cannot change enable_default_gateway for a '
f'default network ID {self.rg_info.default_net_id}.'
)
check_errors = True
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
if (
self.aparams['enable_default_gateway'] is None
and self.aparams['def_netType'] == "PRIVATE"
):
self.message(
'Check for parameter "enable_default_gateway" failed: '
'enable_default_gateway is required when creating a '
'resource group with a private network.'
)
check_errors = True
if (
self.aparams['enable_default_gateway'] is not None
and self.aparams['def_netType'] != "PRIVATE"
):
self.message(
'Check for parameter "enable_default_gateway" failed: '
'cannot create resource group with enable_default_gateway '
'for a non-private network group ID.'
)
check_errors = True
if check_errors:
self.exit(fail=True)
@@ -496,7 +560,8 @@ class decort_rg(DecortController):
' the "rg_name" parameter was not specified.'
)
else:
self.create()
self.check_amodule_args_for_create()
self.create()
if amodule.params['access'] and not amodule.check_mode:
self.access()
elif amodule.params['state'] in ('disabled'):

View File

@@ -70,7 +70,7 @@ class DecortRGList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -47,6 +47,7 @@ class DecortSDNLogicalPort(DecortController):
'labels',
'port_security',
'segment_id',
'no_addresses',
)
logical_port_id: str | None = None

View File

@@ -301,10 +301,11 @@ class DecortSecurityGroup(DecortController):
match aparam_rules['mode']:
case self.SecurityGroupRuleMode.delete.value:
for rule in rules:
self.security_group_detele_rule(
security_group_id=self.id,
rule_id=rule['id'],
)
self.sdk_checkmode(
self.api.cloudapi.security_group.delete_rule)(
security_group_id=self.id,
rule_id=rule['id'],
)
case self.SecurityGroupRuleMode.match.value:
for rule in rules:
if rule.get('id') is None:
@@ -318,10 +319,11 @@ class DecortSecurityGroup(DecortController):
)
rules_ids_to_delete = sg_rules_ids - aparam_rules_ids
for rule_id in rules_ids_to_delete:
self.security_group_detele_rule(
security_group_id=self.id,
rule_id=rule_id,
)
self.sdk_checkmode(
self.api.cloudapi.security_group.delete_rule)(
security_group_id=self.id,
rule_id=rule_id,
)
case self.SecurityGroupRuleMode.update.value:
for rule in rules:
if rule.get('id') is None:

View File

@@ -63,7 +63,7 @@ class DecortSecurityGroupList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -73,7 +73,7 @@ class DecortStoragePolicyList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -54,7 +54,7 @@ class DecortTrunkList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -154,6 +154,17 @@ class decort_vins(DecortController):
warning=True,
)
enable_default_gateway = self.amodule.params['enable_default_gateway']
if enable_default_gateway is None:
enable_default_gateway = True
self.message(
msg=self.MESSAGES.default_value_used(
param_name='enable_default_gateway',
default_value=enable_default_gateway
),
warning=True,
)
self.vins_id = self.vins_provision(
vins_name=self.amodule.params['vins_name'],
account_id=self.acc_id,
@@ -164,6 +175,7 @@ class decort_vins(DecortController):
desc=self.amodule.params['description'],
zone_id=self.amodule.params['zone_id'],
security_group_mode=security_group_mode,
enable_default_gateway=enable_default_gateway,
)
if self.vins_id:
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
@@ -233,11 +245,20 @@ class decort_vins(DecortController):
aparam_zone_id is not None
and aparam_zone_id != self.vins_info.zone_id
):
self.vins_migrate_to_zone(
net_id=self.vins_info.id,
self.sdk_checkmode(self.api.cloudapi.vins.migrate_to_zone)(
vins_id=self.vins_info.id,
zone_id=aparam_zone_id,
)
if (
self.amodule.params['enable_default_gateway']
!= self.vins_info.enable_default_gateway
):
self.sdk_checkmode(self.api.cloudbroker.vins.update)(
vins_id=self.vins_info.id,
enable_default_gateway=self.amodule.params['enable_default_gateway'], # noqa: E501
)
return
def delete(self):
@@ -383,6 +404,9 @@ class decort_vins(DecortController):
zone_id=dict(
type=int,
),
enable_default_gateway=dict(
type='bool',
),
security_group_mode=dict(
type='bool',
),

View File

@@ -67,7 +67,7 @@ class DecortVINSList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

View File

@@ -456,9 +456,9 @@ class decort_vm(DecortController):
image_id=self.amodule.params['image_id'],
account_id=self.acc_id)
if validated_bdisk_size <= image_facts['size']:
if validated_bdisk_size <= image_facts['size_gb']:
# adjust disk size to the minimum allowed by OS image, which will be used to spin off this Compute
validated_bdisk_size = image_facts['size']
validated_bdisk_size = image_facts['size_gb']
# NOTE: due to a libvirt "feature", that impacts management of a VM created without any network interfaces,
# we create KVM VM in HALTED state.
@@ -495,6 +495,10 @@ class decort_vm(DecortController):
if numa_affinity is None:
numa_affinity = 'none'
clock_sync_mode = self.aparams['clock_sync_mode']
if clock_sync_mode is None:
clock_sync_mode = 'default'
chipset = self.amodule.params['chipset']
if chipset is None:
chipset = 'Q35'
@@ -552,6 +556,8 @@ class decort_vm(DecortController):
zone_id=self.aparams['zone_id'],
storage_policy_id=self.aparams['storage_policy_id'],
os_version=self.aparams['os_version'],
cpu_alignment_profile_name=self.aparams['cpu_alignment_profile_name'],
clock_sync_mode=clock_sync_mode,
)
self.comp_should_exist = True
@@ -614,6 +620,12 @@ class decort_vm(DecortController):
custom_fields=custom_fields_fields,
)
if self.aparams['folder_id'] is not None:
self.sdk_checkmode(self.api.cloudapi.vmfolder.computes_move)(
compute_ids=[self.comp_info['id']],
folder_id=self.aparams['folder_id'],
)
# read in Compute facts once more after all initial setup is complete
_, self.comp_info, _ = self.compute_find(
comp_id=self.comp_id,
@@ -673,6 +685,16 @@ class decort_vm(DecortController):
**self.compute_update_args,
)
folder_id = self.aparams['folder_id']
if (
folder_id is not None
and folder_id != (self.comp_info.get('folder_id') or 0)
):
self.sdk_checkmode(self.api.cloudapi.vmfolder.computes_move)(
compute_ids=[self.comp_info['id']],
folder_id=folder_id,
)
if self.amodule.params['rollback_to'] is not None:
self.sdk_checkmode(self.api.cloudapi.compute.snapshot_rollback)(
vm_id=self.comp_info['id'],
@@ -704,9 +726,9 @@ class decort_vm(DecortController):
vm_disk=disk,
)
):
self.compute_boot_disk(
comp_id=self.comp_info['id'],
boot_disk=aparam_disk_id,
self.sdk_checkmode(self.api.cloudapi.compute.boot_disk_set)(
vm_id=self.comp_info['id'],
disk_id=aparam_disk_id,
)
break
@@ -744,7 +766,7 @@ class decort_vm(DecortController):
_, image_facts = self.image_find(
image_id=self.aparams['image_id'],
)
disk_size = image_facts['size']
disk_size = image_facts['size_gb']
os_version = None
if (
@@ -782,8 +804,8 @@ class decort_vm(DecortController):
compute_custom_fields = self.comp_info['custom_fields']
if aparam_custom_fields['disable']:
if compute_custom_fields is not None:
self.compute_disable_custom_fields(
compute_id=self.comp_info['id'],
self.sdk_checkmode(self.api.ca.compute.delete_custom_fields)(
vm_id=self.comp_info['id'],
)
else:
if compute_custom_fields != aparam_custom_fields['fields']:
@@ -794,8 +816,8 @@ class decort_vm(DecortController):
aparam_zone_id = self.aparams['zone_id']
if aparam_zone_id is not None and aparam_zone_id != self.comp_info['zoneId']:
self.compute_migrate_to_zone(
compute_id=self.comp_id,
self.sdk_checkmode(self.api.cloudapi.compute.migrate_to_zone)(
vm_id=self.comp_id,
zone_id=aparam_zone_id,
)
@@ -806,15 +828,21 @@ class decort_vm(DecortController):
aparam_guest_agent['enabled']
and not self.comp_info['qemu_guest']['enabled']
):
self.compute_guest_agent_enable(vm_id=self.comp_id)
self.sdk_checkmode(
self.api.cloudapi.compute.guest_agent_enable
)(vm_id=self.comp_id)
elif (
aparam_guest_agent['enabled'] is False
and self.comp_info['qemu_guest']['enabled']
):
self.compute_guest_agent_disable(vm_id=self.comp_id)
self.sdk_checkmode(
self.api.cloudapi.compute.guest_agent_disable
)(vm_id=self.comp_id)
if aparam_guest_agent['update_available_commands']:
self.compute_guest_agent_feature_update(vm_id=self.comp_id)
self.sdk_checkmode(
self.api.cloudapi.compute.guest_agent_feature_update
)(vm_id=self.comp_id)
aparam_guest_agent_exec = aparam_guest_agent['exec']
if aparam_guest_agent_exec is not None:
@@ -848,6 +876,28 @@ class decort_vm(DecortController):
vm_id=self.comp_id,
)
cpu_alignment_profile = self.aparams['cpu_alignment_profile_name']
if cpu_alignment_profile:
if (
(
self.comp_info['cpu_alignment_profile']
and cpu_alignment_profile != self.comp_info['cpu_alignment_profile']['name']
)
or not self.comp_info['cpu_alignment_profile']
):
self.sdk_checkmode(self.api.ca.compute.set_cpu_alignment_profile)(
vm_ids=[self.comp_id],
cpu_alignment_profile_name=self.aparams['cpu_alignment_profile_name'],
)
elif (
cpu_alignment_profile == ''
and self.comp_info['cpu_alignment_profile']
):
self.sdk_checkmode(self.api.ca.compute.delete_cpu_alignment_profile)(
vm_ids=[self.comp_id],
)
return
@property
@@ -868,6 +918,7 @@ class decort_vm(DecortController):
'network_interface_naming': 'networkInterfaceNaming',
'hot_resize': 'hotResize',
'os_version': 'os_version',
'clock_sync_mode': 'clock',
}
def get_nested_value(
@@ -944,11 +995,11 @@ class decort_vm(DecortController):
tech_status="",
account_id=0,
rg_id=0,
folder_id=0,
username="",
password="",
public_ips=[], # direct IPs; this list can be empty
private_ips=[], # IPs on ViNSes; usually, at least one IP is listed
nat_ip="", # IP of the external ViNS interface; can be empty.
tags={},
chipset="",
interfaces=[],
@@ -978,6 +1029,7 @@ class decort_vm(DecortController):
ret_dict['tech_status'] = self.comp_info['techStatus']
ret_dict['account_id'] = self.comp_info['accountId']
ret_dict['rg_id'] = self.comp_info['rgId']
ret_dict['folder_id'] = self.comp_info.get('folder_id') or 0
if self.comp_info['tags']:
ret_dict['tags'] = self.comp_info['tags']
# if the VM is an imported VM, then the 'accounts' list may be empty,
@@ -987,16 +1039,9 @@ class decort_vm(DecortController):
ret_dict['password'] = self.comp_info['osUsers'][0]['password']
if self.comp_info['interfaces']:
# We need a list of all ViNSes in the account, which owns this Compute
# to find a ViNS, which may have active external connection. Then
# we will save external IP address of that connection in ret_dict['nat_ip']
for iface in self.comp_info['interfaces']:
if iface['connType'] == "VXLAN": # This is ViNS connection
ret_dict['private_ips'].append(iface['ipAddress'])
# if iface['connId']
# Now we need to check if this ViNS has GW function and external connection.
# If it does - save public IP address of GW VNF in ret_dict['nat_ip']
elif iface['connType'] == "VLAN": # This is direct external network connection
ret_dict['public_ips'].append(iface['ipAddress'])
@@ -1010,6 +1055,7 @@ class decort_vm(DecortController):
ret_dict['disks'] = self.comp_info['disks']
for disk in ret_dict['disks']:
disk['read_only_mode'] = disk.pop('read_only')
if self.is_vm_boot_disk(
vm_chipset=self.comp_info['chipset'],
vm_disk=disk,
@@ -1017,6 +1063,9 @@ class decort_vm(DecortController):
# if it is a boot disk - store its size
ret_dict['disk_size'] = disk['sizeMax']
for snapshot in disk['snapshots']:
snapshot['pool_name'] = snapshot.pop('poolName')
ret_dict['chipset'] = self.comp_info['chipset']
ret_dict['interfaces'] = self.comp_info['interfaces']
@@ -1088,6 +1137,12 @@ class decort_vm(DecortController):
ret_dict['weight'] = self.comp_info['weight']
ret_dict['clock_sync_mode'] = self.comp_info['clock']
ret_dict['cpu_alignment_profile'] = self.comp_info['cpu_alignment_profile']
ret_dict['resized_online'] = self.comp_info['resizedOnline']
return ret_dict
def check_amodule_args_for_create(self):
@@ -1175,6 +1230,20 @@ class decort_vm(DecortController):
if self.check_aparam_zone_id() is False:
check_errors = True
else:
if (
self.aparams['cpu_alignment_profile_name']
and self.aparams['zone_id']
):
zone_info = self.zone_get(self.aparams['zone_id'])
if not zone_info['cpu_alignment_profiles']:
check_errors = True
self.message(
'Check for parameter "zone_id" failed:'
f' zone ID {self.aparams['zone_id']} must contain'
' cpu alignment profiles'
)
if self.aparams['guest_agent'] is not None:
check_errors = True
@@ -1230,6 +1299,17 @@ class decort_vm(DecortController):
'abort_cloning can be specified only for existing compute.'
)
if (
self.aparams['cpu_alignment_profile_name'] is not None
and self.aparams['custom_fields'] is not None
):
check_errors = True
self.message(
'Check for parameter "cpu_alignment_profile_name" failed: '
'cpu_alignment_profile_name cannot be used together '
'with parameter "custom_fields".'
)
if check_errors:
self.exit(fail=True)
@@ -1327,6 +1407,9 @@ class decort_vm(DecortController):
bus_num_hex=dict(
type='str',
),
read_only=dict(
type='bool',
),
),
required_together=[
('pci_slot_num_hex', 'bus_num_hex'),
@@ -1334,6 +1417,9 @@ class decort_vm(DecortController):
),
),
),
folder_id=dict(
type='int',
),
id=dict(
type='int',
default=0,
@@ -1386,6 +1472,9 @@ class decort_vm(DecortController):
net_prefix=dict(
type='int',
),
enable_default_gateway=dict(
type='bool',
),
),
required_if=[
('type', 'VINS', ('id',)),
@@ -1602,6 +1691,18 @@ class decort_vm(DecortController):
abort_cloning=dict(
type='bool',
),
cpu_alignment_profile_name=dict(
type='str',
),
clock_sync_mode=dict(
type='str',
choices=[
'default',
'linux',
'windows',
'none',
],
),
),
supports_check_mode=True,
required_one_of=[
@@ -1652,12 +1753,17 @@ class decort_vm(DecortController):
f'disk {aparam_boot_disk_id} is not attached to '
f'Compute ID {self.comp_id}.'
)
else:
elif aparam_boot_disk_id is not None:
aparam_disk_ids = []
if aparam_disks['objects']:
aparam_disk_ids = [
disk['id'] for disk in aparam_disks['objects']
]
match aparam_disks['mode']:
case 'update':
if (
aparam_boot_disk_id not in comp_disk_ids
and aparam_boot_disk_id not in aparam_disks['ids']
and aparam_boot_disk_id not in aparam_disk_ids
):
check_errors = True
self.message(
@@ -1666,7 +1772,7 @@ class decort_vm(DecortController):
f'to Compute ID {self.comp_id}.'
)
case 'match':
if aparam_boot_disk_id not in aparam_disks['ids']:
if aparam_boot_disk_id not in aparam_disk_ids:
check_errors = True
self.message(
f'Check for parameter "boot.disk_id" failed: '
@@ -1674,7 +1780,7 @@ class decort_vm(DecortController):
f'disks.ids'
)
case 'detach' | 'delete':
if aparam_boot_disk_id in aparam_disks['ids']:
if aparam_boot_disk_id in aparam_disk_ids:
check_errors = True
self.message(
f'Check for parameter "boot.disk_id" failed: '
@@ -1923,6 +2029,22 @@ class decort_vm(DecortController):
if self.check_aparam_zone_id() is False:
check_errors = True
else:
if (
self.aparams['zone_id']
and self.aparams['zone_id'] != self.comp_info['zoneId']
):
zone_info = self.zone_get(self.aparams['zone_id'])
if (
not zone_info['cpu_alignment_profiles']
and self.comp_info['cpu_alignment_profile']
):
check_errors = True
self.message(
'Check for parameter "zone_id" failed: '
f'zone ID {self.aparams['zone_id']} '
'must contain cpu alignment profiles.'
)
if self.check_aparam_guest_agent() is False:
check_errors = True
@@ -1988,6 +2110,18 @@ class decort_vm(DecortController):
'Only networks of type VINS, EXTNET, DPDK, SDN, TRUNK '
'can be enabled or disabled.'
)
if (
network['enable_default_gateway'] is not None
and network['type'] != self.VMNetType.VINS.value
):
check_errors = True
self.message(
'Check for parameter '
'"networks.enable_default_gateway" failed: '
'Can not enable or disable default gateway for network'
f' ID {network['id']} and type {network['type']}. '
'Only networks of type VINS can have default gateway.'
)
if self.check_aparam_cdrom() is False:
check_errors = True
@@ -1998,6 +2132,27 @@ class decort_vm(DecortController):
if self.check_aparam_image_id() is False:
check_errors = True
if self.aparams['cpu_alignment_profile_name'] is not None:
if self.comp_info['snapSets'] or self.comp_info['custom_fields']:
check_errors = True
self.message(
'Check for parameter "cpu_alignment_profile_name" failed: '
'VM should not contain snapshots or custom fields.'
)
if (
self.aparams['boot']
and self.aparams['boot']['disk_redeploy'] is not None
and self.aparams['image_id'] is not None
):
if self.aparams['storage_policy_id'] is None:
check_errors = True
self.message(
f'Check for parameter "storage_policy_id" failed: '
'storage_policy_id must be specified when changing '
'the OS image'
)
if check_errors:
self.exit(fail=True)

235
library/decort_vm_folder.py Normal file
View File

@@ -0,0 +1,235 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vm_folder
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from dataclasses import dataclass
from enum import Enum
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import (
AnsibleModuleParamSet,
DecortController,
)
from dynamix_sdk import exceptions as sdk_exceptions
from dynamix_sdk import types as sdk_types
class VmFolderState(Enum):
absent = 'absent'
@dataclass(frozen=True)
class ParamSet(AnsibleModuleParamSet):
id: int | None
name: str | None
parent_id: int | None
rg_id: int | None
state: VmFolderState | None
class DecortVmFolder(DecortController):
param_set: ParamSet
facts: dict[str, Any]
_id: int | None = None
_vm_folder_info: sdk_types.CloudapiVmfolderGetResultModel | None = None
def __init__(self) -> None:
super().__init__(AnsibleModule(**self.amodule_init_args))
self.param_set = ParamSet.from_dict(self.amodule.params)
self.facts = {}
@property
def amodule_init_args(self) -> dict[str, Any]:
return self.pack_amodule_init_args(
argument_spec=ParamSet.get_arg_spec(),
supports_check_mode=True,
)
@property
def id(self) -> int:
if self._id is None:
raise TypeError
return self._id
@property
def vm_folder_info(self) -> sdk_types.CloudapiVmfolderGetResultModel:
if self._vm_folder_info is None:
try:
vm_folder_info = self.api.cloudapi.vmfolder.get(
folder_id=self.id,
)
except sdk_exceptions.RequestException as e:
if (
e.orig_exception.response is not None
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='vm_folder',
id=self.id,
)
)
self.exit(fail=True)
raise e
self._vm_folder_info = vm_folder_info
return self._vm_folder_info
@DecortController.handle_sdk_exceptions
def run(self) -> None:
if self.param_set.id is not None:
self._id = self.param_set.id
elif self.param_set.name is not None:
self._id = self.find_id_by_name()
if self._id is not None:
self.get_info()
self.change()
else:
self.check_amodule_args_for_create()
self.create()
if self.result['changed']:
self.get_info()
self.exit()
def find_id_by_name(self) -> int | None:
rg_id = self.param_set.rg_id
name = self.param_set.name
if rg_id is None or name is None:
return None
matches: list[int] = []
page = 1
page_size = 100
while True:
folders = self.api.cloudapi.vmfolder.list(
rg_id=rg_id,
page=page,
size=page_size,
)
for folder in folders.data:
if folder.name != name:
continue
if (
self.param_set.parent_id is not None
and folder.parent_id != self.param_set.parent_id
):
continue
matches.append(folder.id)
if not folders.data or len(folders.data) < page_size:
break
if (
folders.entry_count
and page * page_size >= folders.entry_count
):
break
page += 1
if len(matches) > 1:
self.message(
msg='Check for parameter "name" failed: '
'multiple vm folders with this name were found; '
'specify parent_id or id'
)
self.exit(fail=True)
if matches:
return matches[0]
return None
def get_info(self) -> None:
if self._vm_folder_info is not None and not self.amodule.check_mode:
self._vm_folder_info = None
self.facts = self.vm_folder_info.model_dump()
def check_amodule_args_for_create(self) -> None:
check_errors = False
if self.param_set.rg_id is None:
check_errors = True
self.message(
msg='Check for parameter "rg_id" failed: '
'rg_id must be specified when creating '
'a new vm_folder'
)
if self.param_set.name is None:
check_errors = True
self.message(
msg='Check for parameter "name" failed: '
'name must be specified when creating '
'a new vm_folder'
)
if self.param_set.state == VmFolderState.absent:
check_errors = True
self.message(
msg='Check for parameter "state" failed: '
'state can not be "absent" when creating '
'a new vm_folder'
)
if check_errors:
self.exit(fail=True)
def create(self) -> None:
rg_id = self.param_set.rg_id
name = self.param_set.name
if rg_id is None or name is None:
raise TypeError
created_id = self.sdk_checkmode(
self.api.cloudapi.vmfolder.create
)(
rg_id=rg_id,
name=name,
parent_id=self.param_set.parent_id,
)
if created_id:
self._id = created_id
def change(self) -> None:
self.change_state()
self.change_params()
def change_state(self) -> None:
if self.param_set.state == VmFolderState.absent:
self.delete()
def change_params(self) -> None:
if (
self.param_set.name is not None
and self.param_set.name != self.vm_folder_info.name
):
self.sdk_checkmode(self.api.cloudapi.vmfolder.rename)(
folder_id=self.id,
name=self.param_set.name,
)
if (
self.param_set.parent_id is not None
and self.param_set.parent_id != self.vm_folder_info.parent_id
):
self.sdk_checkmode(self.api.cloudapi.vmfolder.move)(
folder_id=self.id,
new_parent_id=self.param_set.parent_id,
)
def delete(self) -> None:
self.sdk_checkmode(self.api.cloudapi.vmfolder.delete)(
folder_id=self.id,
)
self.exit()
def main() -> None:
DecortVmFolder().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,141 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vm_folder_access
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from dataclasses import dataclass
from enum import Enum
from typing import Any
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import (
AnsibleModuleParamSet,
DecortController,
)
from dynamix_sdk import exceptions as sdk_exceptions
from dynamix_sdk import types as sdk_types
class VMFolderAccessState(Enum):
absent = 'absent'
@dataclass(frozen=True)
class ParamSet(AnsibleModuleParamSet):
folder_id: int
user: str
right: sdk_types.AccessTypeForSet | None
state: VMFolderAccessState | None
class DecortVMFolderAccess(DecortController):
param_set: ParamSet
folder_info: sdk_types.CloudapiVmfolderGetResultModel
acl: sdk_types.ACLForVMFolderGetAPIResultNM | None = None
def __init__(self) -> None:
super().__init__(AnsibleModule(**self.amodule_init_args))
self.param_set = ParamSet.from_dict(self.amodule.params)
@property
def amodule_init_args(self) -> dict[str, Any]:
return self.pack_amodule_init_args(
argument_spec=ParamSet.get_arg_spec(),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self) -> None:
self.get_folder()
self.check_amodule_args()
self.change()
if self.result['changed'] and not self.amodule.check_mode:
self.get_folder()
self.package_facts()
self.exit()
def get_folder(self) -> None:
try:
self.folder_info = self.api.cloudapi.vmfolder.get(
folder_id=self.param_set.folder_id,
)
except sdk_exceptions.RequestException as e:
if (
e.orig_exception.response is not None
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='VM folder',
id=self.param_set.folder_id,
)
)
self.exit(fail=True)
raise e
self.acl = self.find_acl()
def find_acl(self) -> sdk_types.ACLForVMFolderGetAPIResultNM | None:
for acl in self.folder_info.acl:
if acl.explicit and acl.user_group_id == self.param_set.user:
return acl
return None
def check_amodule_args(self) -> None:
if (
self.param_set.state is None
and self.param_set.right is None
and self.acl is None
):
self.message(
self.MESSAGES.obj_not_found(
obj='VM folder ACL',
id=self.param_set.user,
)
)
self.exit(fail=True)
def change(self) -> None:
if self.param_set.state == VMFolderAccessState.absent:
self.delete()
return
right = self.param_set.right
if right is None or (
self.acl is not None and self.acl.right == right
):
return
self.sdk_checkmode(self.api.cloudapi.vmfolder.add_acl_access)(
folder_id=self.param_set.folder_id,
user=self.param_set.user,
right=right,
)
def delete(self) -> None:
if self.acl is None:
return
self.sdk_checkmode(self.api.cloudapi.vmfolder.del_acl_access)(
folder_id=self.param_set.folder_id,
user=self.param_set.user,
)
if not self.amodule.check_mode:
self.acl = None
def package_facts(self) -> None:
if self.acl is not None:
self.facts = self.acl.model_dump()
else:
self.facts = {}
def main() -> None:
DecortVMFolderAccess().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,71 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vm_folder_list
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from dataclasses import dataclass
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import (
AnsibleModuleParamSet,
DecortController,
)
@dataclass(frozen=True)
class FilterParam:
rg_id: int
@dataclass(frozen=True)
class PaginationParam:
page: int | None
size: int | None
@dataclass(frozen=True)
class ParamSet(AnsibleModuleParamSet):
filter: FilterParam
pagination: PaginationParam
class DecortVMFolderList(DecortController):
param_set: ParamSet
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.param_set = ParamSet.from_dict(self.amodule.params)
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=ParamSet.get_arg_spec(),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter = self.param_set.filter
aparam_pagination = self.param_set.pagination
self.facts = self.api.cloudapi.vmfolder.list(
rg_id=aparam_filter.rg_id,
page=aparam_pagination.page,
size=aparam_pagination.size,
).model_dump()['data']
def main():
DecortVMFolderList().run()
if __name__ == '__main__':
main()

View File

@@ -36,12 +36,18 @@ class DecortVMList(DecortController):
ext_net_name=dict(
type='str',
),
folder_id=dict(
type='int',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
),
include_descendants=dict(
type='bool',
),
ip_addr=dict(
type='str',
),
@@ -77,7 +83,7 @@ class DecortVMList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),
@@ -129,8 +135,10 @@ class DecortVMList(DecortController):
account_id=aparam_filter['account_id'],
ext_net_id=aparam_filter['ext_net_id'],
ext_net_name=aparam_filter['ext_net_name'],
folder_id=aparam_filter['folder_id'],
id=aparam_filter['id'],
include_deleted=aparam_filter['include_deleted'] or False,
include_descendants=aparam_filter['include_descendants'] or False,
ip_addr=aparam_filter['ip_addr'],
name=aparam_filter['name'],
rg_id=aparam_filter['rg_id'],

View File

@@ -10,6 +10,7 @@ description: See L(Module Documentation,https://repository.basistech.ru/BASIS/de
import time
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
import dynamix_sdk.types as sdk_types
class DecortVMSnapshot(DecortController):
@@ -21,6 +22,7 @@ class DecortVMSnapshot(DecortController):
self.vm_facts: dict
self.aparams_label = self.aparams['label']
self.aparams_vm_id = self.aparams['vm_id']
self.vm_snapshot: sdk_types.SnapshotSetAPIResultNM | None = None
self.vm_id, self.vm_facts, _ = self._compute_get_by_id(
comp_id=self.aparams_vm_id,
@@ -50,11 +52,14 @@ class DecortVMSnapshot(DecortController):
and self.aparams_label is not None
and self.aparams_label not in self.vm_snapshot_labels
):
self.message(
f'Snapshot {self.aparams_label} '
f'not found for VM {self.aparams_vm_id}'
)
self.exit(fail=True)
if self.aparams['state'] == 'absent':
self.exit()
else:
self.message(
f'Snapshot {self.aparams_label} '
f'not found for VM {self.aparams_vm_id}'
)
self.exit(fail=True)
@property
def amodule_init_args(self) -> dict:
@@ -79,6 +84,9 @@ class DecortVMSnapshot(DecortController):
type='int',
required=True,
),
with_memory=dict(
type='bool',
),
),
supports_check_mode=True,
required_if=[
@@ -119,6 +127,7 @@ class DecortVMSnapshot(DecortController):
label = self.new_snapshot_label or self.aparams_label
for snapshot in self.vm_snapshots:
if snapshot.label == label:
self.vm_snapshot = snapshot
self.facts = snapshot.model_dump()
if self.aparams['usage']:
self.facts['stored'] = self.get_snapshot_usage()
@@ -137,9 +146,22 @@ class DecortVMSnapshot(DecortController):
self.abort_merge()
def create(self):
with_memory = self.aparams['with_memory']
if with_memory is None:
with_memory = False
self.message(
msg=self.MESSAGES.default_value_used(
param_name='with_memory',
default_value=with_memory
),
warning=True,
)
self.sdk_checkmode(self.api.cloudapi.compute.snapshot_create)(
vm_id=self.aparams_vm_id,
label=self.new_snapshot_label,
with_memory=with_memory,
)
self.get_info(update_vm_snapshots=True)
@@ -151,10 +173,16 @@ class DecortVMSnapshot(DecortController):
self.facts = {}
def abort_merge(self):
self.snapshot_abort_merge(
self.sdk_checkmode(
self.api.cloudapi.compute.abort_shared_snapshot_merge
)(
vm_id=self.aparams_vm_id,
label=self.aparams_label,
)
self.message(
f'Merge aborted for snapshot {self.aparams_label} of VM ID '
f'{self.aparams_vm_id}.'
)
self.get_info()
def get_snapshot_usage(self) -> int:
@@ -178,7 +206,33 @@ class DecortVMSnapshot(DecortController):
'Merge can be aborted only for VM in "MERGE" tech status.'
)
if (
self.aparams['with_memory'] is not None
and self.vm_snapshot
):
if self.aparams['with_memory'] != bool(
self.vm_snapshot.memory_dump_image_id
):
check_errors = True
self.message(
'Check for parameter "with_memory" failed: '
'Changing the "with_memory" parameter is not '
'allowed for an existing snapshot.'
)
if (
self.vm_facts['techStatus'] != 'STARTED'
and self.aparams['with_memory']
):
check_errors = True
self.message(
'Check for parameter "with_memory" failed: '
f'VM ID {self.aparams_vm_id} must be started '
'to create snapshot with memory dump.'
)
if check_errors:
self.facts = {}
self.exit(fail=True)

View File

@@ -61,7 +61,7 @@ class DecortZoneList(DecortController):
),
size=dict(
type='int',
default=50,
default=100,
),
),
),

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
ansible==11.6.0
dacite==1.9.2
requests==2.32.3
git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@1.5.latest
git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@2.0.latest