12.0.0
This commit is contained in:
171
CHANGELOG.md
171
CHANGELOG.md
@@ -1,11 +1,174 @@
|
||||
# Список изменений в версии 11.0.3
|
||||
# Список изменений в версии 12.0.0
|
||||
|
||||
## Добавлено
|
||||
### Глобально
|
||||
| Идентификатор<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-1208 | Модуль завершал работу ошибкой запроса к API при попытке модуля пересоздать группу воркеров. |
|
||||
| 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`. |
|
||||
|
||||
### Модуль decort_vm
|
||||
| Идентификатор<br>задачи | Описание |
|
||||
| --- | --- |
|
||||
| BANS-1088 | Добавлено возвращаемое значение `weight`. |
|
||||
|
||||
### Модуль decort_disk_list
|
||||
| Идентификатор<br>задачи | Описание |
|
||||
| --- | --- |
|
||||
| BANS-1083 | Добавлен параметр `filter.vm_id`. |
|
||||
| BANS-1084 | Добавлен параметр `filter.rg_id`. |
|
||||
| BANS-1096 | Добавлено возвращаемое значение `independent`. |
|
||||
| BANS-1103 | Добавлено возвращаемое значение `provision`. |
|
||||
|
||||
### Модуль decort_image
|
||||
| Идентификатор<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`. |
|
||||
|
||||
## Удалено
|
||||
### Глобально
|
||||
| Идентификатор<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` для объекта, который не найден или безвозвратно удален. |
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
| Версия платформы | Версия модулей Ansible |
|
||||
|:----------------:|:--------------------------:|
|
||||
| 4.6.0 | 12.0.x |
|
||||
| 4.5.0 | 11.0.x |
|
||||
| 4.4.0 | 10.0.x |
|
||||
| 4.4.0 build 963 | 9.0.x |
|
||||
|
||||
@@ -7,9 +7,9 @@ module: decort_account
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Iterable
|
||||
from typing import Any, Iterable
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
from ansible.module_utils.decort_utils import DecortController, sdk_types
|
||||
|
||||
|
||||
class DecortAccount(DecortController):
|
||||
@@ -23,7 +23,7 @@ class DecortAccount(DecortController):
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
access_emails=dict(
|
||||
send_access_emails=dict(
|
||||
type='bool',
|
||||
),
|
||||
acl=dict(
|
||||
@@ -45,8 +45,13 @@ class DecortAccount(DecortController):
|
||||
options=dict(
|
||||
rights=dict(
|
||||
type='str',
|
||||
choices=['R', 'RCX', 'ARCXDU'],
|
||||
default='R',
|
||||
choices=(
|
||||
sdk_types.AccessTypeForSet
|
||||
._member_names_
|
||||
),
|
||||
default=(
|
||||
sdk_types.AccessTypeForSet.R.name
|
||||
),
|
||||
),
|
||||
id=dict(
|
||||
type='str',
|
||||
@@ -69,19 +74,19 @@ class DecortAccount(DecortController):
|
||||
quotas=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
cpu=dict(
|
||||
cpu_count=dict(
|
||||
type='int',
|
||||
),
|
||||
disks_size=dict(
|
||||
storage_size_gb=dict(
|
||||
type='int',
|
||||
),
|
||||
gpu=dict(
|
||||
gpu_count=dict(
|
||||
type='int',
|
||||
),
|
||||
public_ip=dict(
|
||||
ext_ip_count=dict(
|
||||
type='int',
|
||||
),
|
||||
ram=dict(
|
||||
ram_size_mb=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
@@ -135,7 +140,7 @@ class DecortAccount(DecortController):
|
||||
# Parameters or combinations of parameters that can
|
||||
# cause changing the object.
|
||||
changing_params = [
|
||||
'access_emails',
|
||||
'send_access_emails',
|
||||
'acl',
|
||||
['id', 'name'],
|
||||
'quotas',
|
||||
@@ -188,7 +193,6 @@ class DecortAccount(DecortController):
|
||||
self.acc_id, self._acc_info = self.account_find(
|
||||
account_name=self.aparams['name'],
|
||||
account_id=self.aparams['id'],
|
||||
resource_consumption=self.aparams['get_resource_consumption'],
|
||||
)
|
||||
# If this is a repeated getting info
|
||||
else:
|
||||
@@ -196,39 +200,231 @@ class DecortAccount(DecortController):
|
||||
# request info again
|
||||
if not self.amodule.check_mode:
|
||||
self.acc_id, self._acc_info = self.account_find(
|
||||
account_id=self.acc_id,
|
||||
resource_consumption=(
|
||||
self.aparams['get_resource_consumption']
|
||||
),
|
||||
account_id=self._acc_info.id,
|
||||
)
|
||||
self.facts = self.acc_info
|
||||
|
||||
if self._acc_info is not None:
|
||||
acc_info_dict = self.acc_info.model_dump()
|
||||
if self.aparams['get_resource_consumption']:
|
||||
resource_consumption_dict = (
|
||||
self.api.cloudapi.account.get_resource_consumption(
|
||||
account_id=self.acc_info.id,
|
||||
).model_dump()
|
||||
)
|
||||
resource_consumption_dict.pop('id')
|
||||
resource_consumption_dict.pop('quotas')
|
||||
acc_info_dict['resource_consumption'] = (
|
||||
resource_consumption_dict
|
||||
)
|
||||
self.facts = acc_info_dict
|
||||
|
||||
def change(self):
|
||||
self.change_state()
|
||||
|
||||
self.change_acl()
|
||||
|
||||
if self.account_update_args:
|
||||
self.account_update(account_id=self.acc_id,
|
||||
**self.account_update_args)
|
||||
need_account_update_api_call: bool = False
|
||||
|
||||
sdk_param_send_access_emails = None
|
||||
aparam_send_access_emails: bool | None = self.aparams[
|
||||
'send_access_emails'
|
||||
]
|
||||
if (
|
||||
aparam_send_access_emails is not None
|
||||
and self.acc_info.send_access_emails != aparam_send_access_emails
|
||||
):
|
||||
sdk_param_send_access_emails = aparam_send_access_emails
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_name = None
|
||||
aparam_name: str | None = self.aparams['name']
|
||||
if (
|
||||
self.aparams['id'] and aparam_name
|
||||
and self.acc_info.name != aparam_name
|
||||
):
|
||||
sdk_param_name = aparam_name
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_cpu_count_quota = None
|
||||
sdk_param_storage_size_quota_gb = None
|
||||
sdk_param_gpu_count_quota = None
|
||||
sdk_param_ext_ip_count_quota = None
|
||||
sdk_param_ram_size_quota_mb = None
|
||||
aparam_quotas: dict[str, Any] = self.aparams['quotas']
|
||||
if aparam_quotas:
|
||||
aparam_quotas_cpu_count: int | None = aparam_quotas['cpu_count']
|
||||
if (
|
||||
aparam_quotas_cpu_count is not None
|
||||
and self.acc_info.quotas.cpu_count != aparam_quotas_cpu_count
|
||||
):
|
||||
sdk_param_cpu_count_quota = aparam_quotas_cpu_count
|
||||
need_account_update_api_call = True
|
||||
|
||||
aparam_quotas_storage_size_gb: int | None = aparam_quotas[
|
||||
'storage_size_gb'
|
||||
]
|
||||
if (
|
||||
aparam_quotas_storage_size_gb is not None
|
||||
and (
|
||||
self.acc_info.quotas.storage_size_gb
|
||||
!= aparam_quotas_storage_size_gb
|
||||
)
|
||||
):
|
||||
sdk_param_storage_size_quota_gb = aparam_quotas_storage_size_gb
|
||||
need_account_update_api_call = True
|
||||
|
||||
aparam_quotas_gpu_count: int | None = aparam_quotas['gpu_count']
|
||||
if (
|
||||
aparam_quotas_gpu_count is not None
|
||||
and self.acc_info.quotas.gpu_count != aparam_quotas_gpu_count
|
||||
):
|
||||
sdk_param_gpu_count_quota = aparam_quotas_gpu_count
|
||||
need_account_update_api_call = True
|
||||
|
||||
aparam_quotas_ext_ip_count: int | None = aparam_quotas[
|
||||
'ext_ip_count'
|
||||
]
|
||||
if (
|
||||
aparam_quotas_ext_ip_count is not None
|
||||
and (
|
||||
self.acc_info.quotas.ext_ip_count
|
||||
!= aparam_quotas_ext_ip_count
|
||||
)
|
||||
):
|
||||
sdk_param_ext_ip_count_quota = aparam_quotas_ext_ip_count
|
||||
need_account_update_api_call = True
|
||||
|
||||
aparam_quotas_ram_size_mb: int | None = aparam_quotas[
|
||||
'ram_size_mb'
|
||||
]
|
||||
if (
|
||||
aparam_quotas_ram_size_mb is not None
|
||||
and (
|
||||
self.acc_info.quotas.ram_size_mb
|
||||
!= aparam_quotas_ram_size_mb
|
||||
)
|
||||
):
|
||||
sdk_param_ram_size_quota_mb = aparam_quotas_ram_size_mb
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_sep_pools = None
|
||||
aparam_sep_pools: list[dict[str, Any]] = self.aparams['sep_pools']
|
||||
if aparam_sep_pools is not None:
|
||||
sep_pools: set[str] = set()
|
||||
for sep in aparam_sep_pools:
|
||||
sep_pool_names: list[str] = sep['pool_names']
|
||||
for pool_name in sep_pool_names:
|
||||
sep_pools.add(f'{sep["sep_id"]}_{pool_name}')
|
||||
if set(self.acc_info.sep_pools) != sep_pools:
|
||||
sdk_param_sep_pools = list(sep_pools)
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_description = None
|
||||
aparam_desc: str | None = self.aparams['description']
|
||||
if (
|
||||
aparam_desc is not None
|
||||
and self.acc_info.description != aparam_desc
|
||||
):
|
||||
sdk_param_description = aparam_desc
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_default_zone_id = None
|
||||
aparam_default_zone_id: int | None = self.aparams['default_zone_id']
|
||||
if (
|
||||
aparam_default_zone_id is not None
|
||||
and self.acc_info.default_zone_id != aparam_default_zone_id
|
||||
):
|
||||
sdk_param_default_zone_id = aparam_default_zone_id
|
||||
need_account_update_api_call = True
|
||||
|
||||
if need_account_update_api_call:
|
||||
OBJ = 'account'
|
||||
|
||||
self.sdk_checkmode(self.api.ca.account.update)(
|
||||
account_id=self.acc_info.id,
|
||||
cpu_count_quota=sdk_param_cpu_count_quota,
|
||||
gpu_count_quota=sdk_param_gpu_count_quota,
|
||||
name=sdk_param_name,
|
||||
ext_ip_count_quota=sdk_param_ext_ip_count_quota,
|
||||
ram_size_quota_mb=sdk_param_ram_size_quota_mb,
|
||||
send_access_emails=sdk_param_send_access_emails,
|
||||
storage_size_quota_gb=sdk_param_storage_size_quota_gb,
|
||||
sep_pools=sdk_param_sep_pools,
|
||||
description=sdk_param_description,
|
||||
default_zone_id=sdk_param_default_zone_id,
|
||||
)
|
||||
|
||||
if sdk_param_send_access_emails is not None:
|
||||
smth = 'sending access emails'
|
||||
if sdk_param_send_access_emails:
|
||||
self.message(
|
||||
self.MESSAGES.obj_smth_enabled(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
smth=smth,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.message(
|
||||
self.MESSAGES.obj_smth_disabled(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
smth=smth,
|
||||
)
|
||||
)
|
||||
if sdk_param_name is not None:
|
||||
self.message(
|
||||
self.MESSAGES.obj_renamed(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
new_name=sdk_param_name,
|
||||
)
|
||||
)
|
||||
quotas = {
|
||||
'CPU count quota': sdk_param_cpu_count_quota,
|
||||
'storage size quota GB': sdk_param_storage_size_quota_gb,
|
||||
'GPU count quota': sdk_param_gpu_count_quota,
|
||||
'ext IP count quota': sdk_param_ext_ip_count_quota,
|
||||
'RAM size quota MB': sdk_param_ram_size_quota_mb,
|
||||
}
|
||||
for q_name, q_value in quotas.items():
|
||||
if q_value is not None:
|
||||
self.message(
|
||||
self.MESSAGES.obj_smth_changed(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
smth=q_name,
|
||||
new_value=q_value
|
||||
)
|
||||
)
|
||||
|
||||
if sdk_param_default_zone_id is not None:
|
||||
self.message(
|
||||
self.MESSAGES.obj_smth_changed(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
smth='default_zone_id',
|
||||
new_value=sdk_param_default_zone_id,
|
||||
)
|
||||
)
|
||||
|
||||
self.get_info()
|
||||
|
||||
def change_state(self):
|
||||
match self._acc_info:
|
||||
case None:
|
||||
if self._acc_info is None:
|
||||
self.message(self.MESSAGES.obj_not_found(obj=self.OBJ))
|
||||
match self.aparams:
|
||||
case {'state': 'absent' | 'absent_permanently'}:
|
||||
pass
|
||||
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
||||
self.exit(fail=True)
|
||||
case {'status': 'DESTROYED'}:
|
||||
elif self._acc_info.status == sdk_types.AccountStatus.DESTROYED:
|
||||
match self.aparams:
|
||||
case {'state': 'absent' | 'absent_permanently'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj=self.OBJ,
|
||||
id=self.acc_id,
|
||||
id=self.acc_info.id,
|
||||
permanently=True,
|
||||
already=True,
|
||||
)
|
||||
@@ -236,16 +432,16 @@ class DecortAccount(DecortController):
|
||||
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_restored(obj=self.OBJ,
|
||||
id=self.acc_id)
|
||||
id=self.acc_info.id)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
case {'status': 'DELETED'}:
|
||||
elif self._acc_info.status == sdk_types.AccountStatus.DELETED:
|
||||
match self.aparams:
|
||||
case {'state': 'absent'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj=self.OBJ,
|
||||
id=self.acc_id,
|
||||
id=self.acc_info.id,
|
||||
permanently=False,
|
||||
already=True,
|
||||
)
|
||||
@@ -257,7 +453,7 @@ class DecortAccount(DecortController):
|
||||
case {'state': 'disabled'}:
|
||||
self.restore()
|
||||
self.disable()
|
||||
case {'status': 'CONFIRMED'}:
|
||||
elif self._acc_info.status == sdk_types.AccountStatus.CONFIRMED:
|
||||
match self.aparams:
|
||||
case {'state': 'absent'}:
|
||||
self.delete()
|
||||
@@ -267,7 +463,7 @@ class DecortAccount(DecortController):
|
||||
pass
|
||||
case {'state': 'disabled'}:
|
||||
self.disable()
|
||||
case {'status': 'DISABLED'}:
|
||||
elif self._acc_info.status == sdk_types.AccountStatus.DISABLED:
|
||||
match self.aparams:
|
||||
case {'state': 'absent'}:
|
||||
self.delete()
|
||||
@@ -279,19 +475,26 @@ class DecortAccount(DecortController):
|
||||
pass
|
||||
|
||||
def delete(self, permanently=False):
|
||||
self.account_delete(account_id=self.acc_id, permanently=permanently)
|
||||
self.account_delete(
|
||||
account_id=self.acc_info.id,
|
||||
permanently=permanently
|
||||
)
|
||||
self.get_info()
|
||||
|
||||
def disable(self):
|
||||
self.account_disable(account_id=self.acc_id)
|
||||
self.sdk_checkmode(self.api.ca.account.disable)(
|
||||
account_id=self.acc_info.id
|
||||
)
|
||||
self.get_info()
|
||||
|
||||
def enable(self):
|
||||
self.account_enable(account_id=self.acc_id)
|
||||
self.sdk_checkmode(self.api.ca.account.enable)(
|
||||
account_id=self.acc_info.id
|
||||
)
|
||||
self.get_info()
|
||||
|
||||
def restore(self):
|
||||
self.account_restore(account_id=self.acc_id)
|
||||
self.account_restore(account_id=self.acc_info.id)
|
||||
self.get_info()
|
||||
|
||||
def change_acl(self):
|
||||
@@ -299,7 +502,7 @@ class DecortAccount(DecortController):
|
||||
return
|
||||
|
||||
actual_users = {
|
||||
u['userGroupId']: u['right'] for u in self.acc_info['acl']
|
||||
u.user_name: u.access_type for u in self.acc_info.acl
|
||||
}
|
||||
actual_users_ids = set(actual_users.keys())
|
||||
|
||||
@@ -328,8 +531,8 @@ class DecortAccount(DecortController):
|
||||
aparams_users_ids.intersection(actual_users_ids)
|
||||
upd_users = dict()
|
||||
for id in upd_users_ids:
|
||||
if actual_users[id] == 'CXDRAU':
|
||||
actual_user_rights = 'ARCXDU'
|
||||
if actual_users[id] == sdk_types.AccessType.CXDRAU:
|
||||
actual_user_rights = sdk_types.AccessTypeForSet.ARCXDU
|
||||
else:
|
||||
actual_user_rights = actual_users[id]
|
||||
|
||||
@@ -341,68 +544,12 @@ class DecortAccount(DecortController):
|
||||
actual_users_ids.difference(aparams_users_ids)
|
||||
|
||||
if del_users_ids or new_users or upd_users:
|
||||
self.account_change_acl(account_id=self.acc_id,
|
||||
self.account_change_acl(account_id=self.acc_info.id,
|
||||
del_users=del_users_ids,
|
||||
add_users=new_users,
|
||||
upd_users=upd_users)
|
||||
self.get_info()
|
||||
|
||||
@property
|
||||
def account_update_args(self) -> dict:
|
||||
result_args = dict()
|
||||
|
||||
aparam_access_emails = self.aparams['access_emails']
|
||||
if (aparam_access_emails is not None
|
||||
and self.acc_info['sendAccessEmails'] != aparam_access_emails):
|
||||
result_args['access_emails'] = aparam_access_emails
|
||||
|
||||
aparam_name = self.aparams['name']
|
||||
if (self.aparams['id'] and aparam_name
|
||||
and self.acc_info['name'] != aparam_name):
|
||||
result_args['name'] = aparam_name
|
||||
|
||||
aparam_quotas = self.aparams['quotas']
|
||||
if aparam_quotas:
|
||||
quotas_naming = [
|
||||
['cpu', 'CU_C', 'cpu_quota'],
|
||||
['disks_size', 'CU_DM', 'disks_size_quota'],
|
||||
['gpu', 'gpu_units', 'gpu_quota'],
|
||||
['public_ip', 'CU_I', 'public_ip_quota'],
|
||||
['ram', 'CU_M', 'ram_quota'],
|
||||
]
|
||||
for aparam, info_key, result_arg in quotas_naming:
|
||||
current_value = int(self.acc_info['resourceLimits'][info_key])
|
||||
if (aparam_quotas[aparam] is not None
|
||||
and current_value != aparam_quotas[aparam]):
|
||||
result_args[result_arg] = aparam_quotas[aparam]
|
||||
|
||||
aparam_sep_pools = self.aparams['sep_pools']
|
||||
if aparam_sep_pools is not None:
|
||||
sep_pools = set()
|
||||
for sep in aparam_sep_pools:
|
||||
for pool_name in sep['pool_names']:
|
||||
sep_pools.add(
|
||||
f'{sep["sep_id"]}_{pool_name}'
|
||||
)
|
||||
if set(self.acc_info['uniqPools']) != sep_pools:
|
||||
result_args['sep_pools'] = sep_pools
|
||||
|
||||
aparam_desc = self.aparams['description']
|
||||
if (
|
||||
aparam_desc is not None
|
||||
and self.acc_info['description'] != aparam_desc
|
||||
):
|
||||
result_args['description'] = aparam_desc
|
||||
|
||||
aparam_default_zone_id = self.aparams['default_zone_id']
|
||||
if (
|
||||
aparam_default_zone_id is not None
|
||||
and self.acc_info['defaultZoneId'] != aparam_default_zone_id
|
||||
):
|
||||
result_args['default_zone_id'] = aparam_default_zone_id
|
||||
|
||||
return result_args
|
||||
|
||||
def check_aparam_default_zone_id(self) -> bool | None:
|
||||
aparam_default_zone_id = self.aparams['default_zone_id']
|
||||
if aparam_default_zone_id is not None:
|
||||
|
||||
@@ -39,21 +39,21 @@ class decort_bservice(DecortController):
|
||||
self.fail_json(**self.result)
|
||||
# fail the module -> exit
|
||||
# now validate RG
|
||||
validated_rg_id, validated_rg_facts = self.rg_find(
|
||||
validated_rg_id, validated_rg_model = self.rg_find(
|
||||
arg_account_id=validated_acc_id,
|
||||
arg_rg_id=arg_amodule.params['rg_id'],
|
||||
arg_rg_name=arg_amodule.params['rg_name']
|
||||
)
|
||||
if not validated_rg_id:
|
||||
if not validated_rg_id or not validated_rg_model:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||
arg_amodule.params['rg_name'])
|
||||
self.fail_json(**self.result)
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
arg_amodule.params['rg_id'] = validated_rg_id
|
||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
||||
validated_acc_id = validated_rg_facts['accountId']
|
||||
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,
|
||||
@@ -104,11 +104,11 @@ class decort_bservice(DecortController):
|
||||
return
|
||||
|
||||
def create(self):
|
||||
self.bservice_id = self.bservice_id = self.bservice_provision(
|
||||
self.amodule.params['name'],
|
||||
self.amodule.params['rg_id'],
|
||||
self.amodule.params['sshuser'],
|
||||
self.amodule.params['sshkey'],
|
||||
self.bservice_id = self.sdk_checkmode(self.api.ca.bservice.create)(
|
||||
name=self.amodule.params['name'],
|
||||
rg_id=self.amodule.params['rg_id'],
|
||||
ssh_user_name=self.amodule.params['sshuser'],
|
||||
ssh_public_key=self.amodule.params['sshkey'],
|
||||
zone_id=self.aparams['zone_id'],
|
||||
)
|
||||
if self.bservice_id:
|
||||
@@ -136,7 +136,10 @@ class decort_bservice(DecortController):
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
self.bservice_delete(self.bservice_id)
|
||||
self.sdk_checkmode(self.api.ca.bservice.delete)(
|
||||
bservice_id=self.bservice_id,
|
||||
permanently=True,
|
||||
)
|
||||
self.bservice_info['status'] = 'DELETED'
|
||||
self.bservice_should_exist = False
|
||||
return
|
||||
|
||||
@@ -58,6 +58,7 @@ class decort_disk(DecortController):
|
||||
name=arg_amodule.params['name'] if "name" in arg_amodule.params else "",
|
||||
account_id=self.acc_id,
|
||||
check_state=False,
|
||||
fail_if_not_found=False,
|
||||
)
|
||||
|
||||
if arg_amodule.params['place_with']:
|
||||
@@ -67,18 +68,30 @@ class decort_disk(DecortController):
|
||||
self.disk_id = validated_disk_id
|
||||
self.disk_info = validated_disk_facts
|
||||
|
||||
if self.disk_id:
|
||||
self.acc_id = validated_disk_facts['accountId']
|
||||
if self.disk_id and self.disk_info.status not in [
|
||||
sdk_types.DiskStatus.DESTROYED,
|
||||
sdk_types.DiskStatus.PURGED,
|
||||
]:
|
||||
self.acc_id = validated_disk_facts.account_id
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
elif (
|
||||
self.amodule.params['state'] == 'present'
|
||||
and not arg_amodule.params['id']
|
||||
):
|
||||
self.check_amodule_args_for_create()
|
||||
|
||||
def compare_iotune_params(self, new_iotune: dict, current_iotune: dict):
|
||||
def compare_iotune_params(
|
||||
self,
|
||||
new_iotune: dict,
|
||||
current_iotune: sdk_types.IOTuneAPIResultNM,
|
||||
):
|
||||
io_fields = sdk_types.IOTuneAPIResultNM.model_fields.keys()
|
||||
|
||||
for field in io_fields:
|
||||
new_value = new_iotune.get(field)
|
||||
current_value = current_iotune.get(field)
|
||||
if new_value is None:
|
||||
continue
|
||||
current_value = getattr(current_iotune, field, None)
|
||||
|
||||
if new_value != current_value:
|
||||
return False
|
||||
@@ -115,6 +128,10 @@ class decort_disk(DecortController):
|
||||
)
|
||||
#IO tune
|
||||
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
|
||||
if (
|
||||
aparam_limit_io
|
||||
and any(value is not None for value in aparam_limit_io.values())
|
||||
):
|
||||
self.limit_io(aparam_limit_io=aparam_limit_io)
|
||||
#set share status
|
||||
if self.amodule.params['shareable']:
|
||||
@@ -133,13 +150,13 @@ class decort_disk(DecortController):
|
||||
#rename if id present
|
||||
if (
|
||||
self.amodule.params['name'] is not None
|
||||
and self.amodule.params['name'] != self.disk_info['name']
|
||||
and self.amodule.params['name'] != self.disk_info.name
|
||||
):
|
||||
self.rename()
|
||||
#resize
|
||||
if (
|
||||
self.amodule.params['size'] is not None
|
||||
and self.amodule.params['size'] != self.disk_info['sizeMax']
|
||||
and self.amodule.params['size'] != self.disk_info.size_max_gb
|
||||
):
|
||||
self.sdk_checkmode(self.api.cloudapi.disks.resize2)(
|
||||
disk_id=self.disk_id,
|
||||
@@ -147,16 +164,19 @@ class decort_disk(DecortController):
|
||||
)
|
||||
#IO TUNE
|
||||
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
|
||||
if aparam_limit_io:
|
||||
if (
|
||||
aparam_limit_io
|
||||
and any(value is not None for value in aparam_limit_io.values())
|
||||
):
|
||||
if not self.compare_iotune_params(
|
||||
new_iotune=aparam_limit_io,
|
||||
current_iotune=self.disk_info['iotune'],
|
||||
current_iotune=self.disk_info.io_tune,
|
||||
):
|
||||
self.limit_io(aparam_limit_io=aparam_limit_io)
|
||||
|
||||
#share check/update
|
||||
#raise Exception(self.amodule.params['shareable'])
|
||||
if self.amodule.params['shareable'] != self.disk_info['shareable']:
|
||||
if self.amodule.params['shareable'] != self.disk_info.shared:
|
||||
if self.amodule.params['shareable']:
|
||||
self.sdk_checkmode(self.api.cloudapi.disks.share)(
|
||||
disk_id=self.disk_id,
|
||||
@@ -169,7 +189,7 @@ class decort_disk(DecortController):
|
||||
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
||||
if (
|
||||
aparam_storage_policy_id is not None
|
||||
and aparam_storage_policy_id != self.disk_info['storage_policy_id']
|
||||
and aparam_storage_policy_id != self.disk_info.storage_policy_id
|
||||
):
|
||||
self.sdk_checkmode(self.api.ca.disks.change_disk_storage_policy)(
|
||||
disk_id=self.disk_id,
|
||||
@@ -183,7 +203,7 @@ class decort_disk(DecortController):
|
||||
detach=self.amodule.params['force_detach'],
|
||||
permanently=self.amodule.params['permanently'],
|
||||
)
|
||||
self.disk_id, self.disk_info = self._disk_get_by_id(self.disk_id)
|
||||
self.disk_info = self._disk_get_by_id(disk_id=self.disk_id)
|
||||
return
|
||||
|
||||
def rename(self):
|
||||
@@ -199,7 +219,7 @@ class decort_disk(DecortController):
|
||||
self.result['changed'] = False
|
||||
if self.disk_id:
|
||||
self.result['msg'] = ("No state change required for Disk ID {} because of its "
|
||||
"current status '{}'.").format(self.disk_id, self.disk_info['status'])
|
||||
"current status '{}'.").format(self.disk_id, self.disk_info.status)
|
||||
else:
|
||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
||||
"non-existent Disk.").format(self.amodule.params['state'])
|
||||
@@ -219,28 +239,7 @@ class decort_disk(DecortController):
|
||||
if check_mode or self.disk_info is None:
|
||||
return ret_dict
|
||||
|
||||
# remove io param with zero value
|
||||
clean_io = [param for param in self.disk_info['iotune'] if self.disk_info['iotune'][param] == 0]
|
||||
for key in clean_io: del self.disk_info['iotune'][key]
|
||||
|
||||
ret_dict['id'] = self.disk_info['id']
|
||||
ret_dict['name'] = self.disk_info['name']
|
||||
ret_dict['size'] = self.disk_info['sizeMax']
|
||||
ret_dict['state'] = self.disk_info['status']
|
||||
ret_dict['account_id'] = self.disk_info['accountId']
|
||||
ret_dict['sep_id'] = self.disk_info['sepId']
|
||||
ret_dict['pool'] = self.disk_info['pool']
|
||||
ret_dict['computes'] = self.disk_info['computes']
|
||||
ret_dict['gid'] = self.disk_info['gid']
|
||||
ret_dict['iotune'] = self.disk_info['iotune']
|
||||
ret_dict['size_available'] = self.disk_info['sizeAvailable']
|
||||
ret_dict['size_used'] = self.disk_info['sizeUsed']
|
||||
ret_dict['storage_policy_id'] = self.disk_info['storage_policy_id']
|
||||
ret_dict['to_clean'] = self.disk_info['to_clean']
|
||||
ret_dict['cache_mode'] = self.disk_info['cache']
|
||||
ret_dict['blkdiscard'] = self.disk_info['blkdiscard']
|
||||
|
||||
return ret_dict
|
||||
return self.disk_info.model_dump()
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
@@ -392,7 +391,7 @@ class decort_disk(DecortController):
|
||||
if (
|
||||
aparam_storage_policy_id is not None
|
||||
and aparam_storage_policy_id
|
||||
not in self.acc_info['storage_policy_ids']
|
||||
not in self.acc_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -431,24 +430,24 @@ class decort_disk(DecortController):
|
||||
amodule = self.amodule
|
||||
if self.disk_id:
|
||||
#disk exist
|
||||
if self.disk_info['status'] in ["MODELED", "CREATING"]:
|
||||
if self.disk_info.status in [sdk_types.DiskStatus.MODELED, sdk_types.DiskStatus.CREATING]:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("No change can be done for existing Disk ID {} because of its current "
|
||||
"status '{}'").format(self.disk_id, self.disk_info['status'])
|
||||
"status '{}'").format(self.disk_id, self.disk_info.status)
|
||||
# "ASSIGNED","CREATED","DELETED","PURGED", "DESTROYED"
|
||||
elif self.disk_info['status'] in ["ASSIGNED","CREATED"]:
|
||||
elif self.disk_info.status in [sdk_types.DiskStatus.CREATED, sdk_types.DiskStatus.ASSIGNED]:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.delete()
|
||||
elif amodule.params['state'] == 'present':
|
||||
self.action()
|
||||
elif self.disk_info['status'] in ["PURGED", "DESTROYED"]:
|
||||
elif self.disk_info.status in [sdk_types.DiskStatus.PURGED, sdk_types.DiskStatus.DESTROYED]:
|
||||
#re-provision disk
|
||||
if amodule.params['state'] in ('present'):
|
||||
self.create()
|
||||
else:
|
||||
self.nop()
|
||||
elif self.disk_info['status'] == "DELETED":
|
||||
elif self.disk_info.status == sdk_types.DiskStatus.DELETED:
|
||||
if amodule.params['state'] in ('present'):
|
||||
self.action(restore=True)
|
||||
elif (amodule.params['state'] == 'absent' and
|
||||
@@ -459,8 +458,24 @@ class decort_disk(DecortController):
|
||||
else:
|
||||
# preexisting Disk was not found
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.nop()
|
||||
else:
|
||||
self.exit()
|
||||
|
||||
if (
|
||||
(
|
||||
amodule.params['state'] == 'present'
|
||||
or amodule.params['state'] is None
|
||||
)
|
||||
and amodule.params['id']
|
||||
):
|
||||
self.message(
|
||||
f'Disk with ID {amodule.params['id']} not found.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
if (
|
||||
amodule.params['state'] == 'present'
|
||||
and not amodule.params['id']
|
||||
):
|
||||
self.create()
|
||||
|
||||
if self.result['failed']:
|
||||
|
||||
@@ -59,9 +59,11 @@ class DecortDiskList(DecortController):
|
||||
storage_policy_id=dict(
|
||||
type='int',
|
||||
),
|
||||
type=dict(
|
||||
type='str',
|
||||
choices=sdk_types.DiskType._member_names_,
|
||||
rg_id=dict(
|
||||
type='int',
|
||||
),
|
||||
vm_id=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -108,7 +110,6 @@ class DecortDiskList(DecortController):
|
||||
def get_info(self):
|
||||
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||
aparam_status: str | None = aparam_filter['status']
|
||||
aparam_type: str | None = aparam_filter['type']
|
||||
|
||||
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||
|
||||
@@ -137,10 +138,8 @@ class DecortDiskList(DecortController):
|
||||
if aparam_status else None
|
||||
),
|
||||
storage_policy_id=aparam_filter['storage_policy_id'],
|
||||
type=(
|
||||
sdk_types.DiskType[aparam_type]
|
||||
if aparam_type else None
|
||||
),
|
||||
rg_id=aparam_filter['rg_id'],
|
||||
vm_id=aparam_filter['vm_id'],
|
||||
page_number=aparam_pagination['number'],
|
||||
page_size=aparam_pagination['size'],
|
||||
sort_by=sort_by,
|
||||
|
||||
@@ -356,7 +356,7 @@ class decort_group(DecortController):
|
||||
else:
|
||||
if (
|
||||
aparam_storage_policy_id
|
||||
not in self.rg_info['storage_policy_ids']
|
||||
not in self.rg_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -366,7 +366,7 @@ class decort_group(DecortController):
|
||||
)
|
||||
if (
|
||||
aparam_storage_policy_id
|
||||
not in self.acc_info['storage_policy_ids']
|
||||
not in self.acc_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
|
||||
@@ -18,8 +18,8 @@ class decort_image(DecortController):
|
||||
super(decort_image, self).__init__(AnsibleModule(**self.amodule_init_args))
|
||||
amodule = self.amodule
|
||||
|
||||
self.validated_image_id = 0
|
||||
self.validated_virt_image_id = 0
|
||||
self.validated_image_id: int = 0
|
||||
self.validated_virt_image_id: int = 0
|
||||
self.validated_image_name = amodule.params['image_name']
|
||||
self.validated_virt_image_name = None
|
||||
self.image_info: dict
|
||||
@@ -180,7 +180,9 @@ class decort_image(DecortController):
|
||||
|
||||
def decort_image_delete(self,amodule):
|
||||
# function that removes an image
|
||||
self.image_delete(imageId=amodule.image_id_delete)
|
||||
self.sdk_checkmode(self.api.ca.image.delete)(
|
||||
image_id=amodule.image_id_delete
|
||||
)
|
||||
_, image_facts = decort_image._image_get_by_id(self, amodule.image_id_delete)
|
||||
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
||||
return
|
||||
@@ -196,16 +198,23 @@ class decort_image(DecortController):
|
||||
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
||||
return image_id, image_facts
|
||||
|
||||
@DecortController.handle_sdk_exceptions
|
||||
def decort_image_rename(self,amodule):
|
||||
# image renaming function
|
||||
image_facts = self.image_rename(imageId=self.validated_image_id, name=amodule.params['image_name'])
|
||||
self.sdk_checkmode(self.api.ca.image.rename)(
|
||||
image_id=self.validated_image_id,
|
||||
name=amodule.params['image_name'],
|
||||
)
|
||||
self.result['msg'] = ("Image renamed successfully")
|
||||
image_id, image_facts = decort_image.decort_image_find(self, amodule)
|
||||
return image_id, image_facts
|
||||
|
||||
@DecortController.handle_sdk_exceptions
|
||||
def decort_virt_image_rename(self, amodule):
|
||||
image_facts = self.image_rename(imageId=self.validated_virt_image_id,
|
||||
name=amodule.params['virt_name'])
|
||||
self.sdk_checkmode(self.api.ca.image.rename)(
|
||||
image_id=self.validated_virt_image_id,
|
||||
name=amodule.params['virt_name'],
|
||||
)
|
||||
self.result['msg'] = ("Virtual image renamed successfully")
|
||||
image_id, image_facts = self.decort_virt_image_find(amodule)
|
||||
return image_id, image_facts
|
||||
@@ -264,6 +273,8 @@ class decort_image(DecortController):
|
||||
ret_dict['hot_resize'] = arg_image_facts['hotResize']
|
||||
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']
|
||||
return ret_dict
|
||||
|
||||
@property
|
||||
@@ -372,7 +383,7 @@ class decort_image(DecortController):
|
||||
if (
|
||||
aparam_storage_policy_id is not None
|
||||
and aparam_storage_policy_id
|
||||
not in self.acc_info['storage_policy_ids']
|
||||
not in self.acc_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -426,7 +437,7 @@ class decort_image(DecortController):
|
||||
)
|
||||
elif (
|
||||
aparam_storage_policy_id
|
||||
not in self.acc_info['storage_policy_ids']
|
||||
not in self.acc_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
|
||||
@@ -20,7 +20,7 @@ class decort_k8s(DecortController):
|
||||
|
||||
validated_acc_id = 0
|
||||
validated_rg_id = 0
|
||||
validated_rg_facts = None
|
||||
validated_rg_model = None
|
||||
validated_k8ci_id = 0
|
||||
self.k8s_should_exist = False
|
||||
self.is_k8s_stopped_or_will_be_stopped: None | bool = None
|
||||
@@ -55,12 +55,12 @@ class decort_k8s(DecortController):
|
||||
self.amodule.fail_json(**self.result)
|
||||
# fail the module -> exit
|
||||
# now validate RG
|
||||
validated_rg_id, validated_rg_facts = self.rg_find(
|
||||
validated_rg_id, validated_rg_model = self.rg_find(
|
||||
arg_account_id=validated_acc_id,
|
||||
arg_rg_id=arg_amodule.params['rg_id'],
|
||||
arg_rg_name=arg_amodule.params['rg_name']
|
||||
)
|
||||
if not validated_rg_id:
|
||||
if not validated_rg_id or not validated_rg_model:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||
@@ -70,19 +70,21 @@ class decort_k8s(DecortController):
|
||||
|
||||
self.rg_id = validated_rg_id
|
||||
arg_amodule.params['rg_id'] = validated_rg_id
|
||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
||||
self.acc_id = validated_rg_facts['accountId']
|
||||
arg_amodule.params['rg_name'] = validated_rg_model.name
|
||||
self.acc_id = validated_rg_model.account_id
|
||||
|
||||
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=arg_amodule.params['id'],
|
||||
self.k8s_id, self._k8s_info = self.k8s_find(
|
||||
k8s_id=arg_amodule.params['id'],
|
||||
k8s_name=arg_amodule.params['name'],
|
||||
rg_id=validated_rg_id,
|
||||
check_state=False)
|
||||
check_state=False,
|
||||
)
|
||||
|
||||
if self.k8s_id and self.k8s_info['status'] != 'DESTROYED':
|
||||
self.k8s_should_exist = True
|
||||
self.acc_id = self.k8s_info['accountId']
|
||||
self.acc_id = self.k8s_info['account_id']
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
elif arg_amodule.params['state'] != 'absent':
|
||||
self.check_amodule_args_for_create()
|
||||
return
|
||||
|
||||
@@ -96,32 +98,14 @@ class decort_k8s(DecortController):
|
||||
config=None,
|
||||
)
|
||||
|
||||
if self.amodule.params['getConfig'] and self.k8s_info['techStatus'] == "STARTED":
|
||||
ret_dict['config'] = self.k8s_getConfig()
|
||||
|
||||
if check_mode:
|
||||
# in check mode return immediately with the default values
|
||||
return ret_dict
|
||||
|
||||
#if self.k8s_facts is None:
|
||||
# #if void facts provided - change state value to ABSENT and return
|
||||
# ret_dict['state'] = "ABSENT"
|
||||
# return ret_dict
|
||||
|
||||
ret_dict['id'] = self.k8s_info['id']
|
||||
ret_dict['name'] = self.k8s_info['name']
|
||||
ret_dict['techStatus'] = self.k8s_info['techStatus']
|
||||
ret_dict['state'] = self.k8s_info['status']
|
||||
ret_dict['rg_id'] = self.k8s_info['rgId']
|
||||
ret_dict['vins_id'] = self.k8s_vins_id
|
||||
ret_dict['account_id'] = self.acc_id
|
||||
ret_dict['k8s_Masters'] = self.k8s_info['k8sGroups']['masters']
|
||||
ret_dict['k8s_Workers'] = self.k8s_info['k8sGroups']['workers']
|
||||
ret_dict['lb_id'] = self.k8s_info['lbId']
|
||||
ret_dict['description'] = self.k8s_info['desc']
|
||||
ret_dict['zone_id'] = self.k8s_info['zoneId']
|
||||
|
||||
return ret_dict
|
||||
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()
|
||||
return self.k8s_info
|
||||
|
||||
def nop(self):
|
||||
"""No operation (NOP) handler for k8s cluster management by decort_k8s module.
|
||||
@@ -204,10 +188,12 @@ class decort_k8s(DecortController):
|
||||
self.result['failed'] = True
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=k8s_id,
|
||||
self.k8s_id, self._k8s_info = self.k8s_find(
|
||||
k8s_id=k8s_id,
|
||||
k8s_name=self.amodule.params['name'],
|
||||
rg_id=self.rg_id,
|
||||
check_state=False)
|
||||
check_state=False,
|
||||
)
|
||||
|
||||
if self.k8s_id:
|
||||
self.k8s_should_exist = True
|
||||
@@ -219,19 +205,22 @@ class decort_k8s(DecortController):
|
||||
self.aparams['storage_policy_id']
|
||||
),
|
||||
)
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
return
|
||||
|
||||
def destroy(self):
|
||||
self.k8s_delete(self.k8s_id,self.amodule.params['permanent'])
|
||||
self.sdk_checkmode(self.api.ca.k8s.delete)(
|
||||
k8s_id=self.k8s_id,
|
||||
permanently=self.amodule.params['permanent'],
|
||||
)
|
||||
self.k8s_info['status'] = 'DELETED'
|
||||
self.k8s_should_exist = False
|
||||
return
|
||||
|
||||
def action(self, disared_state, preupdate: bool = False):
|
||||
if self.amodule.params['master_chipset'] is not None:
|
||||
for master_node in self.k8s_info['k8sGroups']['masters'][
|
||||
'detailedInfo'
|
||||
for master_node in self.k8s_info['node_groups']['master'][
|
||||
'vms'
|
||||
]:
|
||||
_, master_node_info, _ = self._compute_get_by_id(
|
||||
comp_id=master_node['id']
|
||||
@@ -247,17 +236,27 @@ class decort_k8s(DecortController):
|
||||
self.exit(fail=True)
|
||||
|
||||
if (
|
||||
(
|
||||
self.aparams['name'] is not None
|
||||
and self.aparams['name'] != self.k8s_info['name']
|
||||
)
|
||||
or (
|
||||
self.aparams['description'] is not None
|
||||
and self.aparams['description'] != self.k8s_info['description']
|
||||
)
|
||||
):
|
||||
self.k8s_update(id=self.k8s_id, name=self.aparams['name'])
|
||||
self.sdk_checkmode(self.api.ca.k8s.update)(
|
||||
k8s_id=self.k8s_id,
|
||||
description=self.aparams['description'],
|
||||
name=self.aparams['name'],
|
||||
)
|
||||
|
||||
if preupdate:
|
||||
# K8s info updating
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
#k8s state
|
||||
self.k8s_state(self.k8s_info, disared_state)
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
#check groups and modify if needed
|
||||
if self.aparams['workers'] is not None:
|
||||
self.k8s_workers_modify(
|
||||
@@ -266,14 +265,17 @@ class decort_k8s(DecortController):
|
||||
)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.k8s_info['zoneId']:
|
||||
if (
|
||||
aparam_zone_id is not None
|
||||
and aparam_zone_id != self.k8s_info['zone_id']
|
||||
):
|
||||
self.k8s_migrate_to_zone(
|
||||
k8s_id=self.k8s_id,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
if self.result['changed'] == True:
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
#TODO check workers metadata and modify if needed
|
||||
|
||||
return
|
||||
@@ -413,7 +415,6 @@ class decort_k8s(DecortController):
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
default='Created by decort ansible module',
|
||||
),
|
||||
with_lb=dict(
|
||||
type='bool',
|
||||
@@ -473,25 +474,25 @@ class decort_k8s(DecortController):
|
||||
|
||||
self.is_k8s_stopped_or_will_be_stopped = (
|
||||
(
|
||||
self.k8s_info['techStatus'] == 'STOPPED'
|
||||
self.k8s_info['tech_status'] == 'STOPPED'
|
||||
and (
|
||||
self.aparams['state'] is None
|
||||
or self.aparams['state'] in ('present', 'stopped')
|
||||
)
|
||||
)
|
||||
or (
|
||||
self.k8s_info['techStatus'] != 'STOPPED'
|
||||
self.k8s_info['tech_status'] != 'STOPPED'
|
||||
and self.aparams['state'] == 'stopped'
|
||||
)
|
||||
)
|
||||
|
||||
aparam_sysctl = self.aparams['lb_sysctl']
|
||||
if aparam_sysctl is not None:
|
||||
_, lb_info = self._lb_get_by_id(lb_id=self.k8s_info['lbId'])
|
||||
lb_model = self._lb_get_by_id(lb_id=self.k8s_info['lb_id'])
|
||||
sysctl_with_str_values = {
|
||||
k: str(v) for k, v in aparam_sysctl.items()
|
||||
}
|
||||
if sysctl_with_str_values != lb_info['sysctlParams']:
|
||||
if sysctl_with_str_values != lb_model.sysctl_params:
|
||||
self.message(
|
||||
'Check for parameter "lb_sysctl" failed: '
|
||||
'cannot change lb_sysctl for an existing cluster '
|
||||
@@ -503,7 +504,7 @@ class decort_k8s(DecortController):
|
||||
check_errors = True
|
||||
if (
|
||||
self.aparams['zone_id'] is not None
|
||||
and self.aparams['zone_id'] != self.k8s_info['zoneId']
|
||||
and self.aparams['zone_id'] != self.k8s_info['zone_id']
|
||||
and not self.is_k8s_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
@@ -515,13 +516,11 @@ class decort_k8s(DecortController):
|
||||
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
||||
if aparam_storage_policy_id is not None:
|
||||
computes_ids = []
|
||||
for master_node in self.k8s_info['k8sGroups']['masters'][
|
||||
'detailedInfo'
|
||||
]:
|
||||
for master_node in self.k8s_info['node_groups']['master']['vms']:
|
||||
computes_ids.append(master_node['id'])
|
||||
for wg in self.k8s_info['k8sGroups']['workers']:
|
||||
for wg in self.k8s_info['node_groups']['worker']:
|
||||
workers_ids = [
|
||||
worker['id'] for worker in wg['detailedInfo']
|
||||
worker['id'] for worker in wg['vms']
|
||||
]
|
||||
computes_ids.extend(workers_ids)
|
||||
for compute_id in computes_ids:
|
||||
@@ -589,7 +588,7 @@ class decort_k8s(DecortController):
|
||||
)
|
||||
elif (
|
||||
aparam_storage_policy_id
|
||||
not in self.rg_info['storage_policy_ids']
|
||||
not in self.rg_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -630,7 +629,9 @@ class decort_k8s(DecortController):
|
||||
if amodule.params['state'] in (
|
||||
'disabled', 'enabled', 'present', 'started', 'stopped'
|
||||
):
|
||||
self.k8s_restore(self.k8s_id)
|
||||
self.sdk_checkmode(self.api.ca.k8s.restore)(
|
||||
k8s_id=self.k8s_id,
|
||||
)
|
||||
self.action(disared_state=amodule.params['state'],
|
||||
preupdate=True)
|
||||
if amodule.params['state'] == 'absent':
|
||||
|
||||
@@ -18,11 +18,9 @@ class decort_lb(DecortController):
|
||||
arg_amodule = self.amodule
|
||||
|
||||
self.lb_id = 0
|
||||
self.lb_facts = None
|
||||
self.vins_id = 0
|
||||
self.vins_facts = None
|
||||
self.rg_id = 0
|
||||
self.rg_facts = None
|
||||
self.rg_model = None
|
||||
self.default_server_check = "enabled"
|
||||
self.default_alg = "roundrobin"
|
||||
self.default_settings = {
|
||||
@@ -38,22 +36,34 @@ class decort_lb(DecortController):
|
||||
self.is_lb_stopped_or_will_be_stopped: None | bool = None
|
||||
|
||||
if arg_amodule.params['lb_id']:
|
||||
self.lb_id, self.lb_facts = self.lb_find(arg_amodule.params['lb_id'])
|
||||
if not self.lb_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Specified LB ID {} not found."\
|
||||
.format(arg_amodule.params['lb_id'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.rg_id = self.lb_facts['rgId']
|
||||
self.vins_id = self.lb_facts['vinsId']
|
||||
_, self._lb_info = self.lb_find(lb_id=arg_amodule.params['lb_id'])
|
||||
if self._lb_info is None:
|
||||
if arg_amodule.params['state'] == 'absent':
|
||||
self.nop()
|
||||
self.exit()
|
||||
else:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='lb',
|
||||
id=arg_amodule.params['lb_id'],
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
self.lb_id = self._lb_info.id
|
||||
self.rg_id = self._lb_info.rg_id
|
||||
self.vins_id = self._lb_info.vins_id
|
||||
|
||||
elif arg_amodule.params['rg_id']:
|
||||
self.rg_id, self.rg_facts = self.rg_find(0,arg_amodule.params['rg_id'], arg_rg_name="")
|
||||
if not self.rg_id:
|
||||
self.rg_id, self.rg_model = self.rg_find(
|
||||
0,
|
||||
arg_amodule.params['rg_id'],
|
||||
arg_rg_name="",
|
||||
)
|
||||
if not self.rg_id or not self.rg_model:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Specified RG ID {} not found.".format(arg_amodule.params['rg_id'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.acc_id = self.rg_facts['accountId']
|
||||
self.acc_id = self.rg_model.account_id
|
||||
|
||||
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
|
||||
if not arg_amodule.params['rg_name']:
|
||||
@@ -67,24 +77,22 @@ class decort_lb(DecortController):
|
||||
self.result['msg'] = ("Current user does not have access to the requested account "
|
||||
"or non-existent account specified.")
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.rg_id, self.rg_facts = self.rg_find(self.acc_id,0, arg_rg_name=arg_amodule.params['rg_name'])
|
||||
self.rg_id, self.rg_model = self.rg_find(
|
||||
self.acc_id,
|
||||
0,
|
||||
arg_rg_name=arg_amodule.params['rg_name'],
|
||||
)
|
||||
|
||||
if arg_amodule.params['vins_id']:
|
||||
self.vins_id, self.vins_facts = self.vins_find(
|
||||
self.vins_id = self._vins_get_by_id(
|
||||
vins_id=arg_amodule.params['vins_id']
|
||||
)
|
||||
if not self.vins_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = (
|
||||
f'Specified ViNS ID {arg_amodule.params["vins_id"]}'
|
||||
f' not found'
|
||||
)
|
||||
self.amodule.fail_json(**self.result)
|
||||
).id
|
||||
elif arg_amodule.params['vins_name']:
|
||||
self.vins_id, self.vins_facts = self.vins_find(
|
||||
self.vins_id, _ = self.vins_find(
|
||||
vins_id=arg_amodule.params['vins_id'],
|
||||
vins_name=arg_amodule.params['vins_name'],
|
||||
rg_id=self.rg_id)
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
if not self.vins_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = (
|
||||
@@ -94,10 +102,17 @@ class decort_lb(DecortController):
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
if self.rg_id and arg_amodule.params['lb_name']:
|
||||
self.lb_id, self.lb_facts = self.lb_find(0,arg_amodule.params['lb_name'],self.rg_id)
|
||||
self.lb_id, self._lb_info = self.lb_find(
|
||||
0,
|
||||
arg_amodule.params['lb_name'],
|
||||
self.rg_id,
|
||||
)
|
||||
|
||||
if self.lb_id and self.lb_facts['status'] != 'DESTROYED':
|
||||
self.acc_id = self.lb_facts['accountId']
|
||||
if (
|
||||
self._lb_info
|
||||
and self._lb_info.status != sdk_types.LBStatus.DESTROYED
|
||||
):
|
||||
self.acc_id = self._lb_info.account_id
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
@@ -115,54 +130,73 @@ class decort_lb(DecortController):
|
||||
zone_id=self.aparams['zone_id'],
|
||||
start=start_after_create,
|
||||
)
|
||||
if self.lb_id and (self.amodule.params['backends'] or
|
||||
self.amodule.params['frontends']):
|
||||
self.lb_id, self.lb_facts = self.lb_find(0,self.amodule.params['lb_name'],self.rg_id)
|
||||
if (
|
||||
self.lb_id and (
|
||||
self.amodule.params['backends']
|
||||
or self.amodule.params['frontends']
|
||||
)
|
||||
):
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.lb_update(
|
||||
lb_facts=self.lb_facts,
|
||||
lb_model=self.lb_info,
|
||||
aparam_backends=self.amodule.params['backends'],
|
||||
aparam_frontends=self.amodule.params['frontends'],
|
||||
aparam_servers=self.amodule.params['servers'],
|
||||
)
|
||||
return
|
||||
|
||||
def action(self,d_state='',restore=False):
|
||||
if restore == True:
|
||||
self.lb_restore(lb_id=self.lb_id)
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.lb_state(self.lb_facts, 'enabled')
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
def action(
|
||||
self,
|
||||
d_state='',
|
||||
restore=False,
|
||||
):
|
||||
if restore:
|
||||
self.sdk_checkmode(self.api.ca.lb.restore)(
|
||||
lb_id=self.lb_id,
|
||||
)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.lb_state(self.lb_info, 'enabled')
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
self.lb_update(
|
||||
lb_facts=self.lb_facts,
|
||||
lb_model=self.lb_info,
|
||||
aparam_backends=self.amodule.params['backends'],
|
||||
aparam_frontends=self.amodule.params['frontends'],
|
||||
aparam_servers=self.amodule.params['servers'],
|
||||
aparam_sysctl=self.aparams['sysctl'],
|
||||
)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
if d_state != '':
|
||||
self.lb_state(self.lb_facts, d_state)
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.lb_state(self.lb_info, d_state)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
if (d_state == 'enabled' and
|
||||
self.lb_facts.get('status') == 'ENABLED' and
|
||||
self.lb_facts.get('techStatus') == 'STOPPED'):
|
||||
self.lb_state(self.lb_facts, 'started')
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
if (
|
||||
d_state == 'enabled'
|
||||
and self.lb_info.status == sdk_types.LBStatus.ENABLED
|
||||
and self.lb_info.tech_status == sdk_types.LBTechStatus.STOPPED
|
||||
):
|
||||
self.lb_state(self.lb_info, 'started')
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.lb_facts['zoneId']:
|
||||
if (
|
||||
aparam_zone_id is not None
|
||||
and aparam_zone_id != self.lb_info.zone_id
|
||||
):
|
||||
self.lb_migrate_to_zone(
|
||||
lb_id=self.lb_id,
|
||||
lb_id=self.lb_info.id,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
def delete(self):
|
||||
self.lb_delete(self.lb_id, self.amodule.params['permanently'])
|
||||
self.lb_id, self.lb_facts = self._lb_get_by_id(self.lb_id)
|
||||
self.sdk_checkmode(self.api.ca.lb.delete)(
|
||||
lb_id=self.lb_id,
|
||||
permanently=self.amodule.params['permanently'],
|
||||
)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
return
|
||||
|
||||
def nop(self):
|
||||
@@ -173,30 +207,39 @@ class decort_lb(DecortController):
|
||||
"""
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = False
|
||||
if self.lb_id:
|
||||
self.result['msg'] = ("No state change required for LB ID {} because of its "
|
||||
"current status '{}'.").format(self.lb_id, self.lb_facts['status'])
|
||||
if self._lb_info:
|
||||
self.result['msg'] = (
|
||||
f'No state change required for LB ID {self._lb_info.id} '
|
||||
f'because of its current status "{self._lb_info.status}".'
|
||||
)
|
||||
else:
|
||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
||||
"non-existent LB instance.").format(self.amodule.params['state'])
|
||||
self.result['msg'] = (
|
||||
f'No state change to "{self.amodule.params['state']}" '
|
||||
f'can be done for non-existent LB instance.'
|
||||
)
|
||||
return
|
||||
|
||||
def error(self):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
if self.vins_id:
|
||||
if self._lb_info:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for LB ID {} in the "
|
||||
"current status '{}'").format(self.lb_id,
|
||||
self.amodule.params['state'],
|
||||
self.lb_facts['status'])
|
||||
self.result['msg'] = (
|
||||
f'Invalid target state "{self.amodule.params['state']}" '
|
||||
f'requested for LB ID {self._lb_info.id} in the current status '
|
||||
f'"{self._lb_info.status}".'
|
||||
)
|
||||
else:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for non-existent "
|
||||
"LB name '{}'").format(self.amodule.params['state'],
|
||||
self.amodule.params['lb_name'])
|
||||
self.result['msg'] = (
|
||||
f'Invalid target state "{self.amodule.params['state']}" '
|
||||
f'requested for non-existent LB name '
|
||||
f'"{self.amodule.params['lb_name']}".'
|
||||
)
|
||||
return
|
||||
|
||||
def package_facts(self, arg_check_mode=False):
|
||||
"""Package a dictionary of LB facts according to the decort_lb module specification.
|
||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||
@@ -205,34 +248,16 @@ class decort_lb(DecortController):
|
||||
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode
|
||||
"""
|
||||
|
||||
ret_dict = dict(id=0,
|
||||
name="none",
|
||||
state="CHECK_MODE",
|
||||
sysctl={},
|
||||
)
|
||||
ret_dict = {}
|
||||
|
||||
if arg_check_mode:
|
||||
# in check mode return immediately with the default values
|
||||
return ret_dict
|
||||
|
||||
if self.lb_facts is None:
|
||||
# if void facts provided - change state value to ABSENT and return
|
||||
ret_dict['state'] = "ABSENT"
|
||||
if self._lb_info is None:
|
||||
return ret_dict
|
||||
|
||||
ret_dict['id'] = self.lb_facts['id']
|
||||
ret_dict['name'] = self.lb_facts['name']
|
||||
ret_dict['state'] = self.lb_facts['status']
|
||||
ret_dict['account_id'] = self.lb_facts['accountId']
|
||||
ret_dict['rg_id'] = self.lb_facts['rgId']
|
||||
ret_dict['gid'] = self.lb_facts['gid']
|
||||
if self.amodule.params['state']!="absent":
|
||||
ret_dict['backends'] = self.lb_facts['backends']
|
||||
ret_dict['frontends'] = self.lb_facts['frontends']
|
||||
ret_dict['sysctl'] = self.lb_facts['sysctlParams']
|
||||
ret_dict['zone_id'] = self.lb_facts['zoneId']
|
||||
ret_dict['tech_status'] = self.lb_facts['techStatus']
|
||||
return ret_dict
|
||||
return self._lb_info.model_dump()
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
@@ -321,18 +346,16 @@ class decort_lb(DecortController):
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
|
||||
lb_info: dict = self.lb_facts
|
||||
self.is_lb_stopped_or_will_be_stopped = (
|
||||
(
|
||||
lb_info['techStatus'] == 'STOPPED'
|
||||
self.lb_info.tech_status == sdk_types.LBTechStatus.STOPPED
|
||||
and (
|
||||
self.aparams['state'] is None
|
||||
or self.aparams['state'] in ('present', 'stopped')
|
||||
)
|
||||
)
|
||||
or (
|
||||
lb_info['techStatus'] != 'STOPPED'
|
||||
self.lb_info.tech_status != sdk_types.LBTechStatus.STOPPED
|
||||
and self.aparams['state'] == 'stopped'
|
||||
)
|
||||
)
|
||||
@@ -341,7 +364,7 @@ class decort_lb(DecortController):
|
||||
check_errors = True
|
||||
if (
|
||||
self.aparams['zone_id'] is not None
|
||||
and self.aparams['zone_id'] != lb_info['zoneId']
|
||||
and self.aparams['zone_id'] != self.lb_info.zone_id
|
||||
and not self.is_lb_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
@@ -364,18 +387,32 @@ class decort_lb(DecortController):
|
||||
@DecortController.handle_sdk_exceptions
|
||||
def run(self):
|
||||
amodule = self.amodule
|
||||
if self.lb_id:
|
||||
if self.lb_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING","DESTROYING","RESTORING"]:
|
||||
if self._lb_info:
|
||||
if self._lb_info.status in [
|
||||
sdk_types.LBStatus.MODELED,
|
||||
sdk_types.LBStatus.DISABLING,
|
||||
sdk_types.LBStatus.ENABLING,
|
||||
sdk_types.LBStatus.DELETING,
|
||||
sdk_types.LBStatus.DESTROYING,
|
||||
sdk_types.LBStatus.RESTORING,
|
||||
]:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("No change can be done for existing LB ID {} because of its current "
|
||||
"status '{}'").format(self.lb_id, self.lb_facts['status'])
|
||||
elif self.lb_facts['status'] in ('DISABLED', 'ENABLED', 'CREATED'):
|
||||
self.result['msg'] = (
|
||||
f'No change can be done for existing LB ID '
|
||||
f'{self._lb_info.id} because of its current status '
|
||||
f'"{self._lb_info.status.name}".'
|
||||
)
|
||||
elif self._lb_info.status in [
|
||||
sdk_types.LBStatus.DISABLED,
|
||||
sdk_types.LBStatus.ENABLED,
|
||||
sdk_types.LBStatus.CREATED,
|
||||
]:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.delete()
|
||||
else:
|
||||
self.action(d_state=amodule.params['state'])
|
||||
elif self.lb_facts['status'] == "DELETED":
|
||||
elif self._lb_info.status == sdk_types.LBStatus.DELETED:
|
||||
if amodule.params['state'] == 'present':
|
||||
self.action(restore=True)
|
||||
elif amodule.params['state'] == 'enabled':
|
||||
@@ -385,7 +422,7 @@ class decort_lb(DecortController):
|
||||
self.delete()
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
self.error()
|
||||
elif self.lb_facts['status'] == "DESTROYED":
|
||||
elif self._lb_info.status == sdk_types.LBStatus.DESTROYED:
|
||||
if amodule.params['state'] in ('present', 'enabled'):
|
||||
self.create()
|
||||
elif amodule.params['state'] == 'absent':
|
||||
@@ -407,7 +444,7 @@ class decort_lb(DecortController):
|
||||
amodule.fail_json(**self.result)
|
||||
else:
|
||||
if self.result['changed']:
|
||||
_, self.lb_facts = self.lb_find(lb_id=self.lb_id)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.result['facts'] = self.package_facts(amodule.check_mode)
|
||||
amodule.exit_json(**self.result)
|
||||
|
||||
|
||||
@@ -43,7 +43,13 @@ class decort_pfw(DecortController):
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def decort_pfw_package_facts(self, comp_facts, vins_facts, pfw_facts, check_mode=False):
|
||||
def decort_pfw_package_facts(
|
||||
self,
|
||||
comp_facts,
|
||||
vins_model: sdk_types.CloudapiVinsGetResultModel,
|
||||
pfw_facts,
|
||||
check_mode=False,
|
||||
):
|
||||
"""Package a dictionary of PFW rules facts according to the decort_pfw module specification.
|
||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||
the module run.
|
||||
@@ -68,9 +74,13 @@ class decort_pfw(DecortController):
|
||||
ret_dict['state'] = "ABSENT"
|
||||
return ret_dict
|
||||
|
||||
gw_vnf = vins_model.vnfs.gw
|
||||
ret_dict['compute_id'] = comp_facts['id']
|
||||
ret_dict['vins_id'] = vins_facts['id']
|
||||
ret_dict['public_ip'] = vins_facts['vnfs']['GW']['config']['ext_net_ip']
|
||||
ret_dict['vins_id'] = vins_model.id
|
||||
if gw_vnf:
|
||||
ret_dict['public_ip'] = gw_vnf.config.ext_net_ip
|
||||
else:
|
||||
raise RuntimeError('VINS GW VNF must exist.')
|
||||
|
||||
if len(pfw_facts) != 0:
|
||||
ret_dict['state'] = 'PRESENT'
|
||||
@@ -106,16 +116,15 @@ class decort_pfw(DecortController):
|
||||
self.result['msg'] = "Cannot find specified Compute ID {}.".format(amodule.params['compute_id'])
|
||||
amodule.fail_json(**self.result)
|
||||
|
||||
validated_vins_id, vins_facts = self.vins_find(amodule.params['vins_id'])
|
||||
if not validated_vins_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Cannot find specified ViNS ID {}.".format(amodule.params['vins_id'])
|
||||
amodule.fail_json(**self.result)
|
||||
vins_model = self._vins_get_by_id(vins_id=amodule.params['vins_id'])
|
||||
|
||||
gw_vnf_facts = vins_facts['vnfs'].get('GW')
|
||||
if not gw_vnf_facts or gw_vnf_facts['status'] == "DESTROYED":
|
||||
gw_vnf = vins_model.vnfs.gw
|
||||
if not gw_vnf or gw_vnf.status == sdk_types.VNFStatus.DESTROYED:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "ViNS ID {} does not have a configured external connection.".format(validated_vins_id)
|
||||
self.result['msg'] = (
|
||||
f'ViNS ID {vins_model.id} does not '
|
||||
f'have a configured external connection.'
|
||||
)
|
||||
amodule.fail_json(**self.result)
|
||||
|
||||
#
|
||||
@@ -124,12 +133,20 @@ class decort_pfw(DecortController):
|
||||
|
||||
if amodule.params['state'] == 'absent':
|
||||
# ignore amodule.params['rules'] and remove all rules associated with this Compute
|
||||
pfw_facts = self.pfw_configure(comp_facts, vins_facts, None)
|
||||
pfw_facts = self.pfw_configure(
|
||||
comp_facts,
|
||||
vins_model,
|
||||
None,
|
||||
)
|
||||
elif amodule.params['rules'] is not None:
|
||||
# manage PFW rules accodring to the module arguments
|
||||
pfw_facts = self.pfw_configure(comp_facts, vins_facts, amodule.params['rules'])
|
||||
pfw_facts = self.pfw_configure(
|
||||
comp_facts,
|
||||
vins_model,
|
||||
amodule.params['rules'],
|
||||
)
|
||||
else:
|
||||
pfw_facts = self._pfw_get(comp_facts['id'], vins_facts['id'])
|
||||
pfw_facts = self._pfw_get(comp_facts['id'], vins_model.id)
|
||||
|
||||
#
|
||||
# complete module run
|
||||
@@ -138,7 +155,12 @@ class decort_pfw(DecortController):
|
||||
amodule.fail_json(**self.result)
|
||||
else:
|
||||
# prepare PFW facts to be returned as part of self.result and then call exit_json(...)
|
||||
self.result['facts'] = self.decort_pfw_package_facts(comp_facts, vins_facts, pfw_facts, amodule.check_mode)
|
||||
self.result['facts'] = self.decort_pfw_package_facts(
|
||||
comp_facts,
|
||||
vins_model,
|
||||
pfw_facts,
|
||||
amodule.check_mode,
|
||||
)
|
||||
amodule.exit_json(**self.result)
|
||||
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ class decort_rg(DecortController):
|
||||
amodule = self.amodule
|
||||
|
||||
self.validated_acc_id = 0
|
||||
self.validated_rg_id = 0
|
||||
self.validated_rg_facts = None
|
||||
self.rg_id: int = 0
|
||||
|
||||
if amodule.params['rg_id'] is None:
|
||||
if self.amodule.params['account_id']:
|
||||
@@ -42,13 +41,15 @@ class decort_rg(DecortController):
|
||||
else:
|
||||
self.rg_should_exist = False
|
||||
|
||||
if self.validated_rg_id and self.rg_facts['status'] != 'DESTROYED':
|
||||
if self.rg_id and (
|
||||
self.rg_info.status != sdk_types.ResourceGroupStatus.DESTROYED
|
||||
):
|
||||
self.check_amodule_args_for_change()
|
||||
|
||||
def get_info(self):
|
||||
# If this is the first getting info
|
||||
if not self.validated_rg_id:
|
||||
self.validated_rg_id, self.rg_facts = self.rg_find(
|
||||
if self._rg_info is None:
|
||||
self.rg_id, self._rg_info = self.rg_find(
|
||||
arg_account_id=self.validated_acc_id,
|
||||
arg_rg_id=self.aparams['rg_id'],
|
||||
arg_rg_name=self.aparams['rg_name'],
|
||||
@@ -61,16 +62,16 @@ class decort_rg(DecortController):
|
||||
if self.amodule.check_mode:
|
||||
return
|
||||
|
||||
_, self.rg_facts = self.rg_find(arg_rg_id=self.validated_rg_id)
|
||||
_, self._rg_info = self.rg_find(arg_rg_id=self.rg_id)
|
||||
|
||||
def access(self):
|
||||
should_change_access = False
|
||||
acc_granted = False
|
||||
for rg_item in self.rg_facts['acl']:
|
||||
if rg_item['userGroupId'] == self.amodule.params['access']['user']:
|
||||
for rg_item in self.rg_info.acl:
|
||||
if rg_item.user_name == self.amodule.params['access']['user']:
|
||||
acc_granted = True
|
||||
if self.amodule.params['access']['action'] == 'grant':
|
||||
if rg_item['right'] != self.amodule.params['access']['right']:
|
||||
if rg_item.access_type.value != self.amodule.params['access']['right']:
|
||||
should_change_access = True
|
||||
if self.amodule.params['access']['action'] == 'revoke':
|
||||
should_change_access = True
|
||||
@@ -78,19 +79,30 @@ class decort_rg(DecortController):
|
||||
should_change_access = True
|
||||
|
||||
if should_change_access == True:
|
||||
self.rg_access(self.validated_rg_id, self.amodule.params['access'])
|
||||
self.rg_facts['access'] = self.amodule.params['access']
|
||||
if self.amodule.params['access']['action'] == "grant":
|
||||
self.sdk_checkmode(self.api.ca.rg.access_grant)(
|
||||
access_type=sdk_types.AccessTypeForSet(
|
||||
self.amodule.params['access']['right']
|
||||
),
|
||||
rg_id=self.rg_id,
|
||||
user_name=self.amodule.params['access']['user'],
|
||||
)
|
||||
else:
|
||||
self.sdk_checkmode(self.api.ca.rg.access_revoke)(
|
||||
rg_id=self.rg_id,
|
||||
user_name=self.amodule.params['access']['user'],
|
||||
)
|
||||
self.rg_should_exist = True
|
||||
return
|
||||
|
||||
def error(self):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
if self.validated_rg_id > 0:
|
||||
if self.rg_id:
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for rg ID {} in the "
|
||||
"current status '{}'.").format(self.validated_rg_id,
|
||||
"current status '{}'.").format(self.rg_id,
|
||||
self.amodule.params['state'],
|
||||
self.rg_facts['status'])
|
||||
self.rg_info.status.value)
|
||||
else:
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for non-existent rg name '{}' "
|
||||
"in account ID {} ").format(self.amodule.params['state'],
|
||||
@@ -99,20 +111,31 @@ class decort_rg(DecortController):
|
||||
return
|
||||
|
||||
def update(self):
|
||||
resources = self.rg_facts['Resources']['Reserved']
|
||||
try:
|
||||
rg_res_model = (
|
||||
self.api.cloudapi.rg.get_resource_consumption(rg_id=self.rg_id)
|
||||
)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
self.message(
|
||||
msg=(
|
||||
f'Failed to get RG Resources by ID {self.rg_id}: {e}'
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
reserved_resources = rg_res_model.reserved
|
||||
incorrect_quota = dict(Requested=dict(),
|
||||
Reserved=dict(),)
|
||||
query_key_map = dict(
|
||||
cpu='cpu',
|
||||
ram='ram',
|
||||
disk='disksize',
|
||||
ext_ips='extips',
|
||||
storage_policies='policies',
|
||||
cpu='cpu_count',
|
||||
ram='ram_size_mb',
|
||||
disk='storage_size_gb_by_real_usage',
|
||||
ext_ips='ext_ip_count',
|
||||
storage_policies='storage_policies',
|
||||
)
|
||||
if self.amodule.params['quotas']:
|
||||
for quota_item in self.amodule.params['quotas']:
|
||||
if quota_item == 'storage_policies':
|
||||
rg_storage_policies = resources[query_key_map[quota_item]]
|
||||
rg_storage_policies = reserved_resources.storage_policies
|
||||
aparam_storage_policies = self.amodule.params['quotas'][
|
||||
quota_item
|
||||
]
|
||||
@@ -124,32 +147,35 @@ class decort_rg(DecortController):
|
||||
if (
|
||||
rg_storage_policy
|
||||
and aparam_storage_policy['storage_size_gb']
|
||||
< rg_storage_policy['disksize']
|
||||
< rg_storage_policy.storage_size_gb_by_real_usage
|
||||
):
|
||||
incorrect_quota['Requested'][quota_item] = (
|
||||
self.amodule.params['quotas'][quota_item]
|
||||
)
|
||||
incorrect_quota['Reserved'][quota_item] = (
|
||||
resources[query_key_map[quota_item]]
|
||||
getattr(
|
||||
reserved_resources,
|
||||
query_key_map[quota_item]
|
||||
)
|
||||
)
|
||||
elif (
|
||||
self.amodule.params['quotas'][quota_item]
|
||||
< resources[query_key_map[quota_item]]
|
||||
< getattr(reserved_resources, query_key_map[quota_item])
|
||||
):
|
||||
incorrect_quota['Requested'][quota_item] = (
|
||||
self.amodule.params['quotas'][quota_item]
|
||||
)
|
||||
incorrect_quota['Reserved'][quota_item] = (
|
||||
resources[query_key_map[quota_item]]
|
||||
getattr(reserved_resources, query_key_map[quota_item])
|
||||
)
|
||||
|
||||
if incorrect_quota['Requested']:
|
||||
if reserved_resources and incorrect_quota['Requested']:
|
||||
self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota)
|
||||
self.result['failed'] = True
|
||||
|
||||
if not self.result['failed']:
|
||||
self.rg_update(
|
||||
arg_rg_dict=self.rg_facts,
|
||||
rg_model=self.rg_info,
|
||||
arg_quotas=self.amodule.params['quotas'],
|
||||
arg_res_types=self.amodule.params['resType'],
|
||||
arg_newname=self.amodule.params['rename'],
|
||||
@@ -160,13 +186,15 @@ class decort_rg(DecortController):
|
||||
return
|
||||
|
||||
def setDefNet(self):
|
||||
rg_def_net_type = self.rg_facts['def_net_type']
|
||||
rg_def_net_id = self.rg_facts['def_net_id']
|
||||
rg_def_net_type = self.rg_info.default_net_type.value
|
||||
rg_def_net_id = self.rg_info.default_net_id
|
||||
aparam_def_net_type = self.aparams['def_netType']
|
||||
aparam_def_net_id = self.aparams['def_netId']
|
||||
|
||||
need_to_reset = (aparam_def_net_type == 'NONE'
|
||||
and rg_def_net_type != aparam_def_net_type)
|
||||
need_to_reset = (
|
||||
aparam_def_net_type == sdk_types.RGDefaultNetType.NONE
|
||||
and rg_def_net_type != aparam_def_net_type
|
||||
)
|
||||
|
||||
need_to_change = False
|
||||
if aparam_def_net_id is not None:
|
||||
@@ -174,16 +202,26 @@ class decort_rg(DecortController):
|
||||
or aparam_def_net_type != rg_def_net_type)
|
||||
|
||||
if need_to_reset or need_to_change:
|
||||
self.rg_setDefNet(
|
||||
arg_rg_id=self.validated_rg_id,
|
||||
arg_net_type=aparam_def_net_type,
|
||||
arg_net_id=aparam_def_net_id,
|
||||
if aparam_def_net_type == sdk_types.RGDefaultNetType.NONE:
|
||||
self.sdk_checkmode(self.api.ca.rg.remove_def_net)(
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
else:
|
||||
if aparam_def_net_type == sdk_types.RGDefaultNetType.PRIVATE:
|
||||
net_type = sdk_types.RGDefaultNetTypeForSet.PRIVATE
|
||||
elif aparam_def_net_type == sdk_types.RGDefaultNetType.PUBLIC:
|
||||
net_type = sdk_types.RGDefaultNetTypeForSet.PUBLIC
|
||||
|
||||
self.sdk_checkmode(self.api.ca.rg.set_def_net)(
|
||||
net_type=net_type,
|
||||
rg_id=self.rg_id,
|
||||
net_id=aparam_def_net_id,
|
||||
)
|
||||
self.rg_should_exist = True
|
||||
return
|
||||
|
||||
def create(self):
|
||||
self.validated_rg_id = self.rg_provision(
|
||||
self.rg_id = self.rg_provision(
|
||||
self.validated_acc_id,
|
||||
self.amodule.params['rg_name'],
|
||||
self.amodule.params['owner'],
|
||||
@@ -198,10 +236,10 @@ class decort_rg(DecortController):
|
||||
sdn_access_group_id=self.aparams['sdn_access_group_id'],
|
||||
)
|
||||
|
||||
if self.validated_rg_id:
|
||||
self.validated_rg_id, self.rg_facts = self.rg_find(
|
||||
if self.rg_id:
|
||||
self.rg_id, self._rg_info = self.rg_find(
|
||||
arg_account_id=self.validated_acc_id,
|
||||
arg_rg_id=self.validated_rg_id,
|
||||
arg_rg_id=self.rg_id,
|
||||
arg_rg_name="",
|
||||
arg_check_state=False
|
||||
)
|
||||
@@ -209,31 +247,30 @@ class decort_rg(DecortController):
|
||||
return
|
||||
|
||||
def enable(self):
|
||||
self.rg_enable(self.validated_rg_id,
|
||||
self.amodule.params['state'])
|
||||
if self.amodule.params['state'] == "enabled":
|
||||
self.rg_facts['status'] = 'CREATED'
|
||||
else:
|
||||
self.rg_facts['status'] = 'DISABLED'
|
||||
self.sdk_checkmode(self.api.ca.rg.enable)(
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
elif self.amodule.params['state'] == "disabled":
|
||||
self.sdk_checkmode(self.api.ca.rg.disable)(
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
self.rg_should_exist = True
|
||||
return
|
||||
|
||||
def restore(self):
|
||||
self.rg_restore(self.validated_rg_id)
|
||||
self.rg_facts['status'] = 'DISABLED'
|
||||
self.sdk_checkmode(self.api.ca.rg.restore)(
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
self.rg_should_exist = True
|
||||
return
|
||||
|
||||
def destroy(self):
|
||||
self.rg_delete(
|
||||
rg_id=self.validated_rg_id,
|
||||
self.sdk_checkmode(self.api.ca.rg.delete)(
|
||||
rg_id=self.rg_id,
|
||||
permanently=self.amodule.params['permanently'],
|
||||
recursively=self.aparams['recursive_deletion'],
|
||||
)
|
||||
if self.amodule.params['permanently'] == True:
|
||||
self.rg_facts['status'] = 'DESTROYED'
|
||||
else:
|
||||
self.rg_facts['status'] = 'DELETED'
|
||||
self.rg_should_exist = False
|
||||
return
|
||||
|
||||
@@ -259,23 +296,7 @@ class decort_rg(DecortController):
|
||||
# ret_dict['state'] = "ABSENT"
|
||||
# return ret_dict
|
||||
|
||||
ret_dict['id'] = self.rg_facts['id']
|
||||
ret_dict['name'] = self.rg_facts['name']
|
||||
ret_dict['state'] = self.rg_facts['status']
|
||||
ret_dict['account_id'] = self.rg_facts['accountId']
|
||||
ret_dict['gid'] = self.rg_facts['gid']
|
||||
ret_dict['quota'] = self.rg_facts['resourceLimits']
|
||||
ret_dict['resTypes'] = self.rg_facts['resourceTypes']
|
||||
ret_dict['defNetId'] = self.rg_facts['def_net_id']
|
||||
ret_dict['defNetType'] = self.rg_facts['def_net_type']
|
||||
ret_dict['ViNS'] = self.rg_facts['vins']
|
||||
ret_dict['computes'] = self.rg_facts['vms']
|
||||
ret_dict['uniqPools'] = self.rg_facts['uniqPools']
|
||||
ret_dict['description'] = self.rg_facts['desc']
|
||||
ret_dict['sdn_access_group_id'] = self.rg_facts['sdn_access_group_id']
|
||||
ret_dict['storage_policy_ids'] = self.rg_facts['storage_policy_ids']
|
||||
|
||||
return ret_dict
|
||||
return self.rg_info.model_dump()
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
@@ -290,6 +311,24 @@ class decort_rg(DecortController):
|
||||
),
|
||||
access=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
action=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
choices=[
|
||||
'grant',
|
||||
'revoke',
|
||||
],
|
||||
),
|
||||
user=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
right=dict(
|
||||
type='str',
|
||||
choices=sdk_types.AccessTypeForSet._member_names_,
|
||||
),
|
||||
),
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
@@ -297,12 +336,8 @@ class decort_rg(DecortController):
|
||||
),
|
||||
def_netType=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'PRIVATE',
|
||||
'PUBLIC',
|
||||
'NONE',
|
||||
],
|
||||
default='PRIVATE',
|
||||
choices=sdk_types.RGDefaultNetType._member_names_,
|
||||
default=sdk_types.RGDefaultNetType.PRIVATE.name,
|
||||
),
|
||||
def_netId=dict(
|
||||
type='int',
|
||||
@@ -386,13 +421,13 @@ class decort_rg(DecortController):
|
||||
self.aparams['sdn_access_group_id'] is not None
|
||||
and (
|
||||
self.aparams['sdn_access_group_id']
|
||||
!= self.rg_facts['sdn_access_group_id']
|
||||
!= self.rg_info.sdn_access_group_id
|
||||
)
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "sdn_access_group_id" failed: '
|
||||
'cannot change sdn_access_group_id for an existing resource '
|
||||
f'group ID {self.validated_rg_id}.'
|
||||
f'group ID {self.rg_id}.'
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
@@ -410,10 +445,15 @@ class decort_rg(DecortController):
|
||||
def run(self):
|
||||
amodule = self.amodule
|
||||
#amodule.check_mode=True
|
||||
if self.validated_rg_id > 0:
|
||||
if self.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]:
|
||||
if self.rg_id:
|
||||
if self.rg_info.status in [
|
||||
sdk_types.ResourceGroupStatus.MODELED,
|
||||
sdk_types.ResourceGroupStatus.DISABLING,
|
||||
sdk_types.ResourceGroupStatus.ENABLING,
|
||||
sdk_types.ResourceGroupStatus.DESTROYING,
|
||||
]:
|
||||
self.error()
|
||||
elif self.rg_facts['status'] in ("CREATED"):
|
||||
elif self.rg_info.status == sdk_types.ResourceGroupStatus.CREATED:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.destroy()
|
||||
elif amodule.params['state'] == "disabled":
|
||||
@@ -432,7 +472,7 @@ class decort_rg(DecortController):
|
||||
if amodule.params['def_netType'] is not None:
|
||||
self.setDefNet()
|
||||
|
||||
elif self.rg_facts['status'] == "DELETED":
|
||||
elif self.rg_info.status == sdk_types.ResourceGroupStatus.DELETED:
|
||||
if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True:
|
||||
self.destroy()
|
||||
elif (amodule.params['state'] == 'present'
|
||||
@@ -441,7 +481,7 @@ class decort_rg(DecortController):
|
||||
elif amodule.params['state'] == 'enabled':
|
||||
self.restore()
|
||||
self.enable()
|
||||
elif self.rg_facts['status'] in ("DISABLED"):
|
||||
elif self.rg_info.status == sdk_types.ResourceGroupStatus.DISABLED:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.destroy()
|
||||
elif amodule.params['state'] == ("enabled"):
|
||||
|
||||
213
library/decort_sdn_access_group.py
Normal file
213
library/decort_sdn_access_group.py
Normal file
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_access_group
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNAccessGroups(DecortController):
|
||||
access_group_id: str | None = None
|
||||
_access_group_info: dict[str, Any] | None = None
|
||||
need_final_get: bool = True
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
access_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
comment=dict(
|
||||
type='str',
|
||||
),
|
||||
display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=['present', 'absent'],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def access_group_info(self) -> dict[str, Any]:
|
||||
if self._access_group_info is None:
|
||||
if not isinstance(self.access_group_id, str):
|
||||
raise TypeError
|
||||
access_group_info = self.get()
|
||||
if access_group_info is None:
|
||||
raise TypeError
|
||||
self._access_group_info = access_group_info
|
||||
return self._access_group_info
|
||||
|
||||
def check_amodule_args(self):
|
||||
check_errors = False
|
||||
|
||||
if (
|
||||
self.aparams['state'] == 'absent'
|
||||
and self.aparams['access_group_id'] is None
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "access_group_id" failed: '
|
||||
'access_group_id must be specified when state is "absent".'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
|
||||
if self.aparams['comment'] is None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "comment" failed: parameter must '
|
||||
'be specified for creating an access_group.'
|
||||
)
|
||||
if self.aparams['display_name'] is None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "display_name" failed: parameter must '
|
||||
'be specified for creating an access_group.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def get(self) -> dict[str, Any] | None:
|
||||
params = {'access_group_id': self.access_group_id}
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/access_group/get',
|
||||
arg_params=params,
|
||||
not_fail_codes=[404],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='access_group',
|
||||
id=self.access_group_id,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def access_group_find(self, display_name: str) -> dict[str, Any] | None:
|
||||
params = {'display_name': display_name}
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/access_group/list',
|
||||
arg_params=params,
|
||||
accept_json_response=True,
|
||||
)
|
||||
for access_group in response.json() or []:
|
||||
if access_group['display_name'] == display_name:
|
||||
return access_group
|
||||
return None
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def create(self):
|
||||
params = {
|
||||
'comment': self.aparams['comment'],
|
||||
'display_name': self.aparams['display_name'],
|
||||
}
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.post,
|
||||
arg_api_name='/restmachine/sdn/access_group/create',
|
||||
arg_params=params,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.access_group_id = response.json()['id']
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def delete(self):
|
||||
params = {'access_group_id': self.access_group_id}
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.delete,
|
||||
arg_api_name='/restmachine/sdn/access_group/delete',
|
||||
arg_params=params,
|
||||
not_fail_codes=[204, 404]
|
||||
)
|
||||
self.need_final_get = False
|
||||
if response.status_code == 204:
|
||||
self.set_changed()
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj='access_group',
|
||||
id=self.access_group_id,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='access_group',
|
||||
id=self.access_group_id,
|
||||
)
|
||||
)
|
||||
self.facts = {}
|
||||
|
||||
def run(self):
|
||||
self.check_amodule_args()
|
||||
|
||||
if self.aparams['access_group_id']:
|
||||
self.access_group_id = self.aparams['access_group_id']
|
||||
elif self.aparams['state'] != 'absent':
|
||||
self.check_amodule_args_for_create()
|
||||
access_group_info = self.access_group_find(
|
||||
display_name=self.aparams['display_name'],
|
||||
)
|
||||
if access_group_info:
|
||||
self.access_group_id = access_group_info['id']
|
||||
self._access_group_info = access_group_info
|
||||
|
||||
if self.access_group_id:
|
||||
if self.aparams['state'] == 'absent':
|
||||
self.delete()
|
||||
else:
|
||||
state = self.aparams['state']
|
||||
if state is None:
|
||||
state = 'present'
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='state',
|
||||
default_value=state,
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
|
||||
if state != 'absent':
|
||||
self.create()
|
||||
|
||||
if self.need_final_get:
|
||||
self.facts = self.get()
|
||||
self.exit()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNAccessGroups().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
120
library/decort_sdn_access_group_list.py
Normal file
120
library/decort_sdn_access_group_list.py
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_access_group_list
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNAccessGroupList(DecortController):
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
filter=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
enabled=dict(
|
||||
type='bool',
|
||||
),
|
||||
deleted=dict(
|
||||
type='bool',
|
||||
),
|
||||
display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
owner_display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
created_from=dict(
|
||||
type='str',
|
||||
),
|
||||
created_to=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
),
|
||||
pagination=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
number=dict(
|
||||
type='int',
|
||||
default=1,
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
),
|
||||
sorting=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
asc=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'display_name',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at',
|
||||
'owner_login',
|
||||
],
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.get_info()
|
||||
self.exit()
|
||||
|
||||
def get_info(self):
|
||||
params: dict[str, Any] = dict()
|
||||
|
||||
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||
for field, value in aparam_filter.items():
|
||||
if value is not None:
|
||||
params[field] = value
|
||||
|
||||
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||
params['page'] = aparam_pagination['number']
|
||||
params['per_page'] = aparam_pagination['size']
|
||||
|
||||
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
|
||||
if aparam_sorting:
|
||||
params['sort_by'] = aparam_sorting['field']
|
||||
params['sort_order'] = 'asc' if aparam_sorting['asc'] else 'desc'
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/access_group/list',
|
||||
arg_params=params,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.facts = response.json()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNAccessGroupList().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
160
library/decort_sdn_hypervisor.py
Normal file
160
library/decort_sdn_hypervisor.py
Normal file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_hypervisor
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNHypervisor(DecortController):
|
||||
name: str | None = None
|
||||
_hypervisor_info: dict[str, Any] | None = None
|
||||
need_final_get: bool = True
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
name=dict(
|
||||
type='str',
|
||||
),
|
||||
port_info=dict(
|
||||
type='str',
|
||||
choices=['detailed', 'general'],
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=['absent'],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def hypervisor_info(self) -> dict[str, Any]:
|
||||
if self._hypervisor_info is None:
|
||||
if not isinstance(self.name, str):
|
||||
raise TypeError
|
||||
hypervisor_info = self.get()
|
||||
if hypervisor_info is None:
|
||||
raise TypeError
|
||||
self._hypervisor_info = hypervisor_info
|
||||
return self._hypervisor_info
|
||||
|
||||
def check_amodule_args(self):
|
||||
check_errors = False
|
||||
|
||||
if self.aparams['name'] is None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "name" failed: '
|
||||
'name must be specified.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@DecortController.waypoint
|
||||
def get(self) -> dict[str, Any] | None:
|
||||
params = {'name': self.name}
|
||||
if self.aparams['port_info'] is not None:
|
||||
params['port_info'] = self.aparams['port_info']
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/hypervisor/get',
|
||||
arg_params=params,
|
||||
not_fail_codes=[400],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 400:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='hypervisor',
|
||||
id=self.name,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def update_display_name(self):
|
||||
params = {
|
||||
'name': self.aparams['name'],
|
||||
'display_name': self.aparams['display_name'],
|
||||
}
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.put,
|
||||
arg_api_name='/restmachine/sdn/hypervisor/update_display_name',
|
||||
arg_params=params,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def delete(self):
|
||||
params = {'name': self.name}
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.delete,
|
||||
arg_api_name='/restmachine/sdn/hypervisor/delete',
|
||||
arg_params=params,
|
||||
not_fail_codes=[400]
|
||||
)
|
||||
self.need_final_get = False
|
||||
if response.status_code == 200:
|
||||
self.set_changed()
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj='hypervisor',
|
||||
id=self.name,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='hypervisor',
|
||||
id=self.name,
|
||||
)
|
||||
)
|
||||
self.facts = {}
|
||||
|
||||
def run(self):
|
||||
self.check_amodule_args()
|
||||
self.name = self.aparams['name']
|
||||
|
||||
if self.aparams['state'] == 'absent':
|
||||
self.delete()
|
||||
else:
|
||||
if (
|
||||
self.aparams['display_name'] is not None
|
||||
and self.aparams['display_name']
|
||||
!= self.hypervisor_info.get('display_name')
|
||||
):
|
||||
self.update_display_name()
|
||||
|
||||
if self.need_final_get:
|
||||
self.facts = self.get()
|
||||
self.exit()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNHypervisor().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
135
library/decort_sdn_hypervisor_list.py
Normal file
135
library/decort_sdn_hypervisor_list.py
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_hypervisor_list
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNHypervisorList(DecortController):
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
port_info=dict(
|
||||
type='str',
|
||||
choices=['detailed', 'general']
|
||||
),
|
||||
filter=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
hostname=dict(
|
||||
type='str',
|
||||
),
|
||||
display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
ip=dict(
|
||||
type='str',
|
||||
),
|
||||
created_from=dict(
|
||||
type='str',
|
||||
),
|
||||
created_to=dict(
|
||||
type='str',
|
||||
),
|
||||
updated_from=dict(
|
||||
type='str',
|
||||
),
|
||||
updated_to=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
),
|
||||
pagination=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
number=dict(
|
||||
type='int',
|
||||
default=1,
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
),
|
||||
sorting=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
asc=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'name',
|
||||
'hostname',
|
||||
'last_sync',
|
||||
'display_name',
|
||||
'ip',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
],
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.get_info()
|
||||
self.exit()
|
||||
|
||||
def get_info(self):
|
||||
api_params: dict[str, Any] = dict()
|
||||
|
||||
aparam_port_info = self.aparams['port_info']
|
||||
if aparam_port_info is not None:
|
||||
api_params['port_info'] = aparam_port_info
|
||||
|
||||
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||
for field, value in aparam_filter.items():
|
||||
if value is not None:
|
||||
api_params[field] = value
|
||||
|
||||
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||
api_params['page'] = aparam_pagination['number']
|
||||
api_params['per_page'] = aparam_pagination['size']
|
||||
|
||||
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
|
||||
if aparam_sorting:
|
||||
api_params['sort_by'] = aparam_sorting['field']
|
||||
api_params['sort_order'] = (
|
||||
'asc' if aparam_sorting['asc'] else 'desc'
|
||||
)
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/hypervisor/list',
|
||||
arg_params=api_params,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.facts = response.json()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNHypervisorList().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
588
library/decort_sdn_logical_port.py
Normal file
588
library/decort_sdn_logical_port.py
Normal file
@@ -0,0 +1,588 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_logical_port
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
import time
|
||||
|
||||
|
||||
class DecortSDNLogicalPort(DecortController):
|
||||
FIELDS_FOR_CREATE = (
|
||||
'access_group_id',
|
||||
'adapter_mac',
|
||||
'description',
|
||||
'display_name',
|
||||
'hypervisor',
|
||||
'labels',
|
||||
'port_security',
|
||||
'segment_id',
|
||||
'unique_identifier',
|
||||
)
|
||||
|
||||
FIELDS_FOR_UPDATE = (
|
||||
'adapter_mac',
|
||||
'description',
|
||||
'display_name',
|
||||
'hypervisor',
|
||||
'labels',
|
||||
'logical_port_id',
|
||||
'port_security',
|
||||
'segment_id',
|
||||
'version_id',
|
||||
)
|
||||
|
||||
UPDATE_FIELDS = (
|
||||
'adapter_mac',
|
||||
'description',
|
||||
'display_name',
|
||||
'hypervisor',
|
||||
'labels',
|
||||
'port_security',
|
||||
'segment_id',
|
||||
)
|
||||
|
||||
logical_port_id: str | None = None
|
||||
_logical_port_info: dict[str, Any] | None = None
|
||||
need_final_get: bool = True
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
no_migration=dict(
|
||||
type='bool',
|
||||
),
|
||||
hypervisor_change_via_migration=dict(
|
||||
type='bool',
|
||||
default=False,
|
||||
),
|
||||
logical_port_id=dict(
|
||||
type='str',
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'present',
|
||||
'enabled',
|
||||
'disabled',
|
||||
'absent',
|
||||
'absent_force',
|
||||
],
|
||||
),
|
||||
access_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
adapter_mac=dict(
|
||||
type='str',
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
),
|
||||
display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
hypervisor=dict(
|
||||
type='str',
|
||||
),
|
||||
labels=dict(
|
||||
type='dict',
|
||||
),
|
||||
no_addresses=dict(
|
||||
type='bool',
|
||||
),
|
||||
port_security=dict(
|
||||
type='bool',
|
||||
),
|
||||
segment_id=dict(
|
||||
type='str',
|
||||
),
|
||||
unique_identifier=dict(
|
||||
type='str',
|
||||
),
|
||||
version_id=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[
|
||||
('unique_identifier', 'logical_port_id'),
|
||||
('no_migration', 'hypervisor_change_via_migration'),
|
||||
],
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.check_amodule_args()
|
||||
state = self.aparams['state']
|
||||
if self.aparams['unique_identifier'] is not None:
|
||||
self._logical_port_info = (
|
||||
self.logical_port_get_by_unique_identifier(
|
||||
fail_if_not_found=False,
|
||||
)
|
||||
)
|
||||
self.logical_port_id = self._logical_port_info['id']
|
||||
elif self.aparams['logical_port_id']:
|
||||
self.logical_port_id = self.aparams['logical_port_id']
|
||||
self._logical_port_info = self.logical_port_get(
|
||||
fail_if_not_found=False,
|
||||
)
|
||||
elif self.aparams['display_name']:
|
||||
self._logical_port_info = self.logical_port_find(
|
||||
display_name=self.aparams['display_name']
|
||||
)
|
||||
if self._logical_port_info:
|
||||
self.logical_port_id = self._logical_port_info['id']
|
||||
|
||||
if self._logical_port_info is not None:
|
||||
if state in ('absent', 'absent_force'):
|
||||
self.logical_port_delete(
|
||||
force=state == 'absent_force',
|
||||
)
|
||||
elif self.aparams['no_migration']:
|
||||
self.logical_port_migration_cancel()
|
||||
elif self.aparams['hypervisor_change_via_migration']:
|
||||
self.logical_port_migration_start()
|
||||
else:
|
||||
self.logical_port_update(
|
||||
desired_enabled=(
|
||||
True if state == 'enabled'
|
||||
else False if state == 'disabled'
|
||||
else None
|
||||
)
|
||||
)
|
||||
else:
|
||||
if state == 'present':
|
||||
self.check_amodule_args_for_create()
|
||||
self.logical_port_create()
|
||||
elif state in ('enabled', 'disabled'):
|
||||
self.message(
|
||||
'Check for parameter "state" failed: state values '
|
||||
'"enabled"/"disabled" can only be applied to an existing '
|
||||
'logical port.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
else:
|
||||
self.need_final_get = False
|
||||
self.facts = {}
|
||||
|
||||
if self.need_final_get and not self.amodule.check_mode:
|
||||
if self._logical_port_info is not None:
|
||||
self.facts = self.logical_port_info
|
||||
else:
|
||||
self.facts = self.logical_port_get()
|
||||
self.exit()
|
||||
|
||||
def check_amodule_args(self):
|
||||
check_errors = False
|
||||
|
||||
if (
|
||||
self.aparams['no_migration']
|
||||
and self.aparams['state'] == 'absent'
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "no_migration" failed: '
|
||||
'no_migration cannot be used when state is "absent".'
|
||||
)
|
||||
|
||||
if (
|
||||
self.aparams['hypervisor_change_via_migration']
|
||||
and self.aparams['hypervisor'] is None
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameters '
|
||||
'"hypervisor_change_via_migration/hypervisor" failed: '
|
||||
'"hypervisor" must be specified when '
|
||||
'"hypervisor_change_via_migration" is true.'
|
||||
)
|
||||
|
||||
if (
|
||||
self.aparams['no_migration']
|
||||
and self.aparams['hypervisor'] is not None
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameters "no_migration/hypervisor" failed: '
|
||||
'"hypervisor" cannot be used when "no_migration" is true.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
|
||||
for field in (
|
||||
'access_group_id',
|
||||
'description',
|
||||
'display_name',
|
||||
'hypervisor',
|
||||
'port_security',
|
||||
'segment_id',
|
||||
):
|
||||
if self.aparams[field] is None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "{field}" failed: parameter '
|
||||
f'"{field}" is required when creating a logical port.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@property
|
||||
def logical_port_info(self) -> dict[str, Any]:
|
||||
if self._logical_port_info is None:
|
||||
if (
|
||||
self.aparams['unique_identifier'] is None
|
||||
and self.aparams['logical_port_id'] is None
|
||||
):
|
||||
raise TypeError
|
||||
logical_port_info = self.logical_port_get()
|
||||
if logical_port_info is None:
|
||||
raise TypeError
|
||||
self._logical_port_info = logical_port_info
|
||||
return self._logical_port_info
|
||||
|
||||
@DecortController.waypoint
|
||||
def logical_port_get_by_unique_identifier(
|
||||
self,
|
||||
fail_if_not_found: bool = True,
|
||||
) -> dict[str, Any] | None:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name=(
|
||||
'/restmachine/sdn/logical_port/get_by_unique_identifier'
|
||||
),
|
||||
arg_params={
|
||||
'unique_identifier': self.aparams['unique_identifier']
|
||||
},
|
||||
not_fail_codes=[404],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
if fail_if_not_found:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='logical port',
|
||||
id=self.aparams['unique_identifier'],
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return None
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
def logical_port_get(
|
||||
self,
|
||||
fail_if_not_found: bool = True,
|
||||
) -> dict[str, Any] | None:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/logical_port/get',
|
||||
arg_params={'logical_port_id': self.logical_port_id},
|
||||
accept_json_response=True,
|
||||
not_fail_codes=[404],
|
||||
)
|
||||
if response.status_code == 404:
|
||||
if fail_if_not_found:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='logical port',
|
||||
id=self.logical_port_id,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return None
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
def logical_port_find(self, display_name: str) -> dict[str, Any] | None:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/logical_port/list',
|
||||
arg_params={'display_name': display_name},
|
||||
accept_json_response=True,
|
||||
)
|
||||
return response.json()[0] if response.json() else None
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def logical_port_create(self):
|
||||
payload = {}
|
||||
for field in self.FIELDS_FOR_CREATE:
|
||||
value = self.aparams[field]
|
||||
if value is not None:
|
||||
payload[field] = value
|
||||
payload['enabled'] = True
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.post,
|
||||
arg_api_name='/restmachine/sdn/logical_port/create',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.logical_port_id = response.json()['id']
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def logical_port_delete(
|
||||
self,
|
||||
force: bool = False,
|
||||
):
|
||||
version_id = self.aparams['version_id']
|
||||
if version_id is None and self.logical_port_info is not None:
|
||||
version_id = self.logical_port_info['version_id']
|
||||
|
||||
payload = {
|
||||
'logical_port_id': self.logical_port_id,
|
||||
'version_id': version_id,
|
||||
'force': force,
|
||||
}
|
||||
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.delete,
|
||||
arg_api_name='/restmachine/sdn/logical_port/delete',
|
||||
arg_json_body=payload,
|
||||
)
|
||||
self.need_final_get = False
|
||||
self.facts = {}
|
||||
self.set_changed()
|
||||
self.message(
|
||||
msg=self.MESSAGES.obj_deleted(
|
||||
obj='logical port',
|
||||
id=self.logical_port_id,
|
||||
permanently=True,
|
||||
)
|
||||
)
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def logical_port_update(
|
||||
self,
|
||||
desired_enabled: bool | None = None,
|
||||
):
|
||||
need_update = False
|
||||
|
||||
addresses_to_remove = []
|
||||
for field in self.UPDATE_FIELDS:
|
||||
value = self.aparams[field]
|
||||
if value is None:
|
||||
continue
|
||||
if self.aparams['no_addresses']:
|
||||
bindings = self.logical_port_info['bindings']
|
||||
if bindings and bindings.get('logical_port_addresses'):
|
||||
for address in bindings['logical_port_addresses']:
|
||||
addresses_to_remove.append(address['id'])
|
||||
if addresses_to_remove:
|
||||
need_update = True
|
||||
|
||||
if field == 'port_security':
|
||||
current_value = self.logical_port_info['bindings'].get(
|
||||
'port_security'
|
||||
)
|
||||
elif field == 'segment_id':
|
||||
current_value = self.logical_port_info['bindings'].get(
|
||||
'segment_id'
|
||||
)
|
||||
else:
|
||||
current_value = self.logical_port_info.get(field)
|
||||
if isinstance(value, dict) and isinstance(current_value, dict):
|
||||
if any(
|
||||
current_value.get(key) != expected_value
|
||||
for key, expected_value in value.items()
|
||||
):
|
||||
need_update = True
|
||||
break
|
||||
continue
|
||||
if value != current_value:
|
||||
need_update = True
|
||||
break
|
||||
|
||||
if (
|
||||
not need_update
|
||||
and desired_enabled is not None
|
||||
and desired_enabled != self.logical_port_info['enabled']
|
||||
):
|
||||
need_update = True
|
||||
|
||||
if need_update:
|
||||
payload = {
|
||||
'logical_port_id': self.logical_port_id,
|
||||
'version_id': (
|
||||
self.aparams['version_id']
|
||||
or self.logical_port_info['version_id']
|
||||
),
|
||||
'adapter_mac': (
|
||||
self.aparams['adapter_mac']
|
||||
or self.logical_port_info['adapter_mac']
|
||||
),
|
||||
'description': (
|
||||
self.aparams['description']
|
||||
or self.logical_port_info['description']
|
||||
),
|
||||
'display_name': (
|
||||
self.aparams['display_name']
|
||||
or self.logical_port_info['display_name']
|
||||
),
|
||||
'enabled': (
|
||||
desired_enabled
|
||||
if desired_enabled is not None
|
||||
else self.logical_port_info['enabled']
|
||||
),
|
||||
'hypervisor': (
|
||||
self.aparams['hypervisor']
|
||||
or self.logical_port_info['hypervisor']
|
||||
),
|
||||
'labels': (
|
||||
self.aparams['labels']
|
||||
or self.logical_port_info.get('labels')
|
||||
),
|
||||
'port_security': (
|
||||
self.aparams['port_security']
|
||||
if self.aparams['port_security'] is not None
|
||||
else self.logical_port_info['bindings']['port_security']
|
||||
),
|
||||
'segment_id': (
|
||||
self.aparams['segment_id']
|
||||
or self.logical_port_info['bindings']['segment_id']
|
||||
),
|
||||
}
|
||||
if addresses_to_remove:
|
||||
payload['remove_addresses'] = addresses_to_remove
|
||||
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.put,
|
||||
arg_api_name='/restmachine/sdn/logical_port/update',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self._logical_port_info = None
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def logical_port_migration_cancel(self):
|
||||
version_id = self.aparams['version_id']
|
||||
if version_id is None and self._logical_port_info is not None:
|
||||
version_id = self._logical_port_info['version_id']
|
||||
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.delete,
|
||||
arg_api_name='/restmachine/sdn/logical_port/migration_cancel',
|
||||
arg_json_body={
|
||||
'logical_port_id': self.logical_port_id,
|
||||
'version_id': version_id,
|
||||
},
|
||||
)
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def logical_port_migration_start(self):
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.post,
|
||||
arg_api_name='/restmachine/sdn/logical_port/migration_start',
|
||||
arg_json_body={
|
||||
'logical_port_id': self.logical_port_id,
|
||||
'target_hypervisor': self.aparams['hypervisor'],
|
||||
'version_id': (
|
||||
self.aparams['version_id']
|
||||
or self.logical_port_info['version_id']
|
||||
if self._logical_port_info is not None
|
||||
else self.aparams['version_id']
|
||||
),
|
||||
},
|
||||
accept_json_response=True,
|
||||
)
|
||||
waiting_statuses = {
|
||||
'Idle',
|
||||
'SynchronizingAtCore',
|
||||
'SynchronizingAtOVN',
|
||||
}
|
||||
failed_statuses = {
|
||||
'NoHypervisorAtOVN': (
|
||||
'Logical port migration failed: no hypervisor at OVN.'
|
||||
),
|
||||
'FailedAtCore': 'Logical port migration failed at core.',
|
||||
'TemporaryFailedAtCore': (
|
||||
'Logical port migration temporary failed at core.'
|
||||
),
|
||||
}
|
||||
target_hypervisor = self.aparams['hypervisor']
|
||||
|
||||
for _ in range(300):
|
||||
logical_port_info = self.logical_port_get()
|
||||
if logical_port_info is None:
|
||||
self.message(
|
||||
'Logical port migration failed: '
|
||||
'can\'t get logical port info.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
status_info = logical_port_info.get('status')
|
||||
if not isinstance(status_info, dict):
|
||||
status_info = {}
|
||||
hypervisors = status_info.get('hypervisors', [])
|
||||
|
||||
target_hypervisor_status = None
|
||||
for hypervisor in hypervisors:
|
||||
if hypervisor.get('name') == target_hypervisor:
|
||||
target_hypervisor_status = str(
|
||||
hypervisor.get('operation_status')
|
||||
)
|
||||
break
|
||||
|
||||
if target_hypervisor_status == 'Synchronized':
|
||||
self._logical_port_info = logical_port_info
|
||||
self.set_changed()
|
||||
return
|
||||
|
||||
if target_hypervisor_status == 'NoHypervisorAtOVN':
|
||||
self.message(failed_statuses['NoHypervisorAtOVN'])
|
||||
self.exit(fail=True)
|
||||
if target_hypervisor_status == 'FailedAtCore':
|
||||
self.message(failed_statuses['FailedAtCore'])
|
||||
self.exit(fail=True)
|
||||
if target_hypervisor_status == 'TemporaryFailedAtCore':
|
||||
self.message(failed_statuses['TemporaryFailedAtCore'])
|
||||
self.exit(fail=True)
|
||||
|
||||
if target_hypervisor_status in waiting_statuses:
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
if target_hypervisor_status is None:
|
||||
self.message(
|
||||
'Logical port migration failed: target hypervisor '
|
||||
f'"{target_hypervisor}" not found in status.'
|
||||
)
|
||||
else:
|
||||
self.message(
|
||||
'Logical port migration failed with unexpected status '
|
||||
f'for hypervisor "{target_hypervisor}": '
|
||||
f'{target_hypervisor_status}.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
self.message('Logical port migration timed out.')
|
||||
self.exit(fail=True)
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNLogicalPort().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
206
library/decort_sdn_logical_port_address.py
Normal file
206
library/decort_sdn_logical_port_address.py
Normal file
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_logical_port_address
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNLogicalPortAddress(DecortController):
|
||||
logical_port_id: str | None = None
|
||||
_logical_port_info: dict[str, Any] | None = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
discovered=dict(
|
||||
type='bool',
|
||||
),
|
||||
ip_addr=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
ip_type=dict(
|
||||
type='str',
|
||||
choices=['IPv4', 'IPv6'],
|
||||
),
|
||||
logical_port_id=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
mac=dict(
|
||||
type='str',
|
||||
),
|
||||
primary=dict(
|
||||
type='bool',
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=['present', 'absent'],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
|
||||
if self.aparams['ip_type'] is None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "ip_type" failed: '
|
||||
'ip_type must be specified when creating a new address.'
|
||||
)
|
||||
|
||||
if self.aparams['mac'] is None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "mac" failed: '
|
||||
'mac must be specified when creating a new address.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@property
|
||||
def logical_port_info(self) -> dict[str, Any]:
|
||||
if self._logical_port_info is None:
|
||||
logical_port_info = self.logical_port_get()
|
||||
if logical_port_info is None:
|
||||
raise TypeError
|
||||
self._logical_port_info = logical_port_info
|
||||
return self._logical_port_info
|
||||
|
||||
def find_address(self) -> dict[str, Any] | None:
|
||||
addresses = self.logical_port_info.get('bindings', {}).get(
|
||||
'logical_port_addresses', []
|
||||
)
|
||||
for addr in addresses:
|
||||
if addr.get('ip') == self.aparams['ip_addr']:
|
||||
return addr
|
||||
return None
|
||||
|
||||
def port_payload(self) -> dict:
|
||||
info = self.logical_port_info
|
||||
return {
|
||||
'logical_port_id': self.logical_port_id,
|
||||
'version_id': info['version_id'],
|
||||
'adapter_mac': info['adapter_mac'],
|
||||
'description': info['description'],
|
||||
'display_name': info['display_name'],
|
||||
'enabled': info['enabled'],
|
||||
'hypervisor': info['hypervisor'],
|
||||
'labels': info.get('labels'),
|
||||
'port_security': info['bindings']['port_security'],
|
||||
'segment_id': info['bindings']['segment_id'],
|
||||
}
|
||||
|
||||
def logical_port_update(self, payload: dict):
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.put,
|
||||
arg_api_name='/restmachine/sdn/logical_port/update',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self._logical_port_info = response.json()
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
def logical_port_get(self) -> dict[str, Any]:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/logical_port/get',
|
||||
arg_params={'logical_port_id': self.logical_port_id},
|
||||
accept_json_response=True,
|
||||
not_fail_codes=[404],
|
||||
)
|
||||
if response.status_code == 404:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='logical port',
|
||||
id=self.logical_port_id,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def address_add(self):
|
||||
payload = self.port_payload()
|
||||
address_data: dict[str, Any] = {'ip': self.aparams['ip_addr']}
|
||||
for param_name, api_param_name in (
|
||||
('ip_type', 'ip_type'),
|
||||
('discovered', 'is_discovered'),
|
||||
('primary', 'is_primary'),
|
||||
('mac', 'mac'),
|
||||
):
|
||||
if self.aparams[param_name] is not None:
|
||||
address_data[api_param_name] = self.aparams[param_name]
|
||||
payload['add_addresses'] = [address_data]
|
||||
self.logical_port_update(payload)
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def address_remove(self, address_id: str):
|
||||
payload = self.port_payload()
|
||||
payload['remove_addresses'] = [address_id]
|
||||
self.logical_port_update(payload)
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj='logical port address',
|
||||
id=self.aparams['ip_addr'],
|
||||
)
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.logical_port_id = self.aparams['logical_port_id']
|
||||
|
||||
existing_addr = self.find_address()
|
||||
|
||||
if existing_addr:
|
||||
if self.aparams['state'] == 'absent':
|
||||
self.address_remove(existing_addr['id'])
|
||||
else:
|
||||
state = self.aparams['state']
|
||||
if state is None:
|
||||
state = 'present'
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='state',
|
||||
default_value=state,
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
if state == 'absent':
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='logical port address',
|
||||
id=self.aparams['ip_addr'],
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
self.address_add()
|
||||
|
||||
self.facts = self.find_address() or {}
|
||||
self.exit()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNLogicalPortAddress().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
171
library/decort_sdn_logical_port_list.py
Normal file
171
library/decort_sdn_logical_port_list.py
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_logical_port_list
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNLogicalPortList(DecortController):
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
filter=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
access_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
adapter_mac=dict(
|
||||
type='str',
|
||||
),
|
||||
address_detection=dict(
|
||||
type='bool',
|
||||
),
|
||||
created_from=dict(
|
||||
type='str',
|
||||
),
|
||||
created_to=dict(
|
||||
type='str',
|
||||
),
|
||||
display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
enabled=dict(
|
||||
type='bool',
|
||||
),
|
||||
external_network_id=dict(
|
||||
type='str',
|
||||
),
|
||||
hypervisor=dict(
|
||||
type='str',
|
||||
),
|
||||
hypervisor_display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
hypervisor_status=dict(
|
||||
type='str',
|
||||
choices=['Up', 'Warning', 'Error'],
|
||||
),
|
||||
live_migration_target_hv=dict(
|
||||
type='str',
|
||||
),
|
||||
operation_status=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'Idle',
|
||||
'SynchronizingAtCore',
|
||||
'SynchronizingAtOVN',
|
||||
'Synchronized',
|
||||
'NoHypervisorAtOVN',
|
||||
'FailedAtCore',
|
||||
'TemporaryFailedAtCore',
|
||||
],
|
||||
),
|
||||
port_security=dict(
|
||||
type='bool',
|
||||
),
|
||||
segment_display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
segment_id=dict(
|
||||
type='str',
|
||||
),
|
||||
unique_identifier=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
),
|
||||
pagination=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
number=dict(
|
||||
type='int',
|
||||
default=1,
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
),
|
||||
),
|
||||
),
|
||||
sorting=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
asc=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'created_at',
|
||||
'deleted_at',
|
||||
'display_name',
|
||||
'hypervisor',
|
||||
'hypervisor_display_name',
|
||||
'port_security',
|
||||
'primary_address',
|
||||
'segment_display_name',
|
||||
'segment_id',
|
||||
'updated_at',
|
||||
],
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.get_info()
|
||||
self.exit()
|
||||
|
||||
def get_info(self):
|
||||
api_params: dict[str, Any] = dict()
|
||||
|
||||
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||
for field, value in aparam_filter.items():
|
||||
if value is not None:
|
||||
api_params[field] = value
|
||||
|
||||
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||
api_params['page'] = aparam_pagination['number']
|
||||
api_params['per_page'] = aparam_pagination['size']
|
||||
|
||||
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
|
||||
if aparam_sorting:
|
||||
api_params['sort_by'] = aparam_sorting['field']
|
||||
api_params['sort_order'] = (
|
||||
'asc' if aparam_sorting['asc'] else 'desc'
|
||||
)
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/logical_port/list',
|
||||
arg_params=api_params,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.facts = response.json()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNLogicalPortList().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
299
library/decort_sdn_network_object_group.py
Normal file
299
library/decort_sdn_network_object_group.py
Normal file
@@ -0,0 +1,299 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_network_object_group
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNNetworkObjectGroup(DecortController):
|
||||
REQUIRED_FIELDS = (
|
||||
'access_group_id',
|
||||
'description',
|
||||
'name',
|
||||
)
|
||||
|
||||
object_group_id: str | None = None
|
||||
_object_group_info: dict[str, Any] | None = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
access_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
),
|
||||
no_addresses=dict(
|
||||
type='bool',
|
||||
),
|
||||
no_logical_ports=dict(
|
||||
type='bool',
|
||||
),
|
||||
name=dict(
|
||||
type='str',
|
||||
),
|
||||
object_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
version_id=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def object_group_info(self) -> dict[str, Any]:
|
||||
if self._object_group_info is None:
|
||||
if not isinstance(self.object_group_id, str):
|
||||
raise TypeError
|
||||
object_group_info = self.network_object_group_get()
|
||||
if object_group_info is None:
|
||||
raise TypeError
|
||||
self._object_group_info = object_group_info
|
||||
return self._object_group_info
|
||||
|
||||
def run(self):
|
||||
if self.aparams['object_group_id']:
|
||||
self.object_group_id = self.aparams['object_group_id']
|
||||
elif self.aparams['name']:
|
||||
object_group_info = self.network_object_group_find(
|
||||
name=self.aparams['name']
|
||||
)
|
||||
if object_group_info:
|
||||
self.object_group_id = object_group_info['id']
|
||||
|
||||
if self.object_group_id is None:
|
||||
self.check_amodule_args_for_create()
|
||||
self.network_object_group_create()
|
||||
else:
|
||||
self.check_amodule_args_for_update()
|
||||
self.network_object_group_update()
|
||||
|
||||
if self.aparams['no_logical_ports']:
|
||||
self.network_object_group_detach_logical_ports()
|
||||
|
||||
self.facts = self.network_object_group_get()
|
||||
self.exit()
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
|
||||
for field in self.REQUIRED_FIELDS:
|
||||
if self.aparams[field] is None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "{field}" failed: '
|
||||
f'"{field}" is required when creating an object group.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_update(self):
|
||||
check_errors = False
|
||||
version_id = self.aparams['version_id']
|
||||
if (
|
||||
version_id is not None
|
||||
and version_id != self.object_group_info['version_id']
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameters "version_id" failed: '
|
||||
'object group version mismatch: '
|
||||
f'given version: {version_id}, '
|
||||
f'current version: {self.object_group_info["version_id"]}.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@DecortController.waypoint
|
||||
def network_object_group_get(self) -> dict[str, Any] | None:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/get',
|
||||
arg_params={'object_group_id': self.object_group_id},
|
||||
not_fail_codes=[404],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
self.message(
|
||||
f'Network object group with id "{self.object_group_id}" '
|
||||
'not found.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def network_object_group_find(self, name: str) -> dict[str, Any] | None:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/list',
|
||||
arg_params={'name': name},
|
||||
accept_json_response=True,
|
||||
)
|
||||
return response.json()[0] if response.json() else None
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def network_object_group_create(self):
|
||||
payload = dict()
|
||||
for field in self.REQUIRED_FIELDS:
|
||||
value = self.aparams[field]
|
||||
if value is not None:
|
||||
payload[field] = value
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.post,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/create',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self._object_group_info = response.json()
|
||||
self.object_group_id = response.json()['id']
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def network_object_group_update(self):
|
||||
need_update = False
|
||||
|
||||
remove_addresses = False
|
||||
if self.aparams['no_addresses']:
|
||||
current_addresses = self.object_group_info.get('addresses')
|
||||
if isinstance(current_addresses, list) and current_addresses:
|
||||
remove_addresses = True
|
||||
need_update = True
|
||||
|
||||
for field in self.REQUIRED_FIELDS:
|
||||
value = self.aparams[field]
|
||||
if value is None:
|
||||
continue
|
||||
current_value = self.object_group_info.get(field)
|
||||
if isinstance(value, list) and isinstance(current_value, list):
|
||||
if len(value) != len(current_value):
|
||||
need_update = True
|
||||
break
|
||||
for index, expected_item in enumerate(value):
|
||||
current_item = current_value[index]
|
||||
if (
|
||||
isinstance(expected_item, dict)
|
||||
and isinstance(current_item, dict)
|
||||
):
|
||||
if any(
|
||||
current_item.get(key) != expected_value
|
||||
for key, expected_value in expected_item.items()
|
||||
):
|
||||
need_update = True
|
||||
break
|
||||
continue
|
||||
if expected_item != current_item:
|
||||
need_update = True
|
||||
break
|
||||
if need_update:
|
||||
break
|
||||
continue
|
||||
if value != current_value:
|
||||
need_update = True
|
||||
break
|
||||
|
||||
if not need_update:
|
||||
return
|
||||
|
||||
payload = {
|
||||
'object_group_id': self.object_group_id,
|
||||
'version_id': (
|
||||
self.aparams['version_id']
|
||||
or self.object_group_info['version_id']
|
||||
),
|
||||
'access_group_id': (
|
||||
self.aparams['access_group_id']
|
||||
or self.object_group_info['access_group_id']
|
||||
),
|
||||
'description': (
|
||||
self.aparams['description']
|
||||
or self.object_group_info['description']
|
||||
),
|
||||
'name': (
|
||||
self.aparams['name']
|
||||
or self.object_group_info['name']
|
||||
),
|
||||
}
|
||||
if remove_addresses:
|
||||
payload['addresses'] = []
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.put,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/update',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self._object_group_info = response.json()
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def network_object_group_detach_logical_ports(self):
|
||||
logical_ports = self.object_group_info.get('logical_ports')
|
||||
if not isinstance(logical_ports, list) or not logical_ports:
|
||||
return
|
||||
|
||||
port_bindings = []
|
||||
for logical_port in logical_ports:
|
||||
if (
|
||||
isinstance(logical_port, dict)
|
||||
and logical_port.get('id') is not None
|
||||
and logical_port.get('version_id') is not None
|
||||
):
|
||||
port_bindings.append(
|
||||
{
|
||||
'port_id': logical_port['id'],
|
||||
'port_version': logical_port['version_id'],
|
||||
}
|
||||
)
|
||||
|
||||
if not port_bindings:
|
||||
return
|
||||
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.post,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/detach_logical_ports', # noqa: E501
|
||||
arg_json_body={
|
||||
'access_group_id': (
|
||||
self.aparams['access_group_id']
|
||||
or self.object_group_info['access_group_id']
|
||||
),
|
||||
'object_group_id': self.object_group_id,
|
||||
'version_id': (
|
||||
self.aparams['version_id']
|
||||
or self.object_group_info['version_id']
|
||||
),
|
||||
'port_bindings': port_bindings,
|
||||
},
|
||||
accept_json_response=True,
|
||||
)
|
||||
self._object_group_info = None
|
||||
self.set_changed()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNNetworkObjectGroup().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
209
library/decort_sdn_network_object_group_ip_range.py
Normal file
209
library/decort_sdn_network_object_group_ip_range.py
Normal file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_network_object_group_ip_range
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNNetworkObjectGroupIPRange(DecortController):
|
||||
object_group_id: str | None = None
|
||||
_object_group_info: dict[str, Any] | None = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
object_group_id=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
ip_addr_range=dict(
|
||||
type='dict',
|
||||
required=True,
|
||||
options=dict(
|
||||
start=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
end=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
),
|
||||
ip_proto=dict(
|
||||
type='str',
|
||||
choices=['IPv4', 'IPv6'],
|
||||
),
|
||||
net_prefix=dict(
|
||||
type='str',
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
choices=['present', 'absent'],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_if=[('state', 'present', ['ip_proto'])],
|
||||
)
|
||||
|
||||
@property
|
||||
def object_group_info(self) -> dict[str, Any]:
|
||||
if self._object_group_info is None:
|
||||
object_group_info = self.network_object_group_get()
|
||||
if object_group_info is None:
|
||||
raise TypeError
|
||||
self._object_group_info = object_group_info
|
||||
return self._object_group_info
|
||||
|
||||
def find_ip_range(self) -> dict | None:
|
||||
ip_addr = self.aparams['ip_addr_range']['start']
|
||||
ip_proto = self.aparams['ip_proto']
|
||||
for addr in self.object_group_info.get('addresses') or []:
|
||||
if addr.get('ip_addr') != ip_addr:
|
||||
continue
|
||||
if (
|
||||
ip_proto is not None
|
||||
and addr.get('net_address_type') != ip_proto
|
||||
):
|
||||
continue
|
||||
return addr
|
||||
return None
|
||||
|
||||
def group_payload(self) -> dict:
|
||||
return {
|
||||
'object_group_id': self.object_group_id,
|
||||
'version_id': self.object_group_info['version_id'],
|
||||
'access_group_id': self.object_group_info['access_group_id'],
|
||||
'description': self.object_group_info['description'],
|
||||
'name': self.object_group_info['name'],
|
||||
}
|
||||
|
||||
def ip_range_add(self):
|
||||
current_addresses = self.object_group_info.get('addresses') or []
|
||||
ip_addr_range = self.aparams['ip_addr_range']
|
||||
ip_proto = self.aparams['ip_proto']
|
||||
|
||||
ip_range_data = {'ip_addr': ip_addr_range['start']}
|
||||
if ip_proto is not None:
|
||||
ip_range_data['net_address_type'] = ip_proto
|
||||
if ip_addr_range['end'] is not None:
|
||||
ip_range_data['ip_addr_range_end'] = ip_addr_range['end']
|
||||
if self.aparams['net_prefix'] is not None:
|
||||
ip_range_data['ip_prefix'] = self.aparams['net_prefix']
|
||||
|
||||
for index, addr in enumerate(current_addresses):
|
||||
if addr.get('ip_addr') != ip_range_data['ip_addr']:
|
||||
continue
|
||||
if (
|
||||
ip_proto is not None
|
||||
and addr.get('net_address_type') != ip_proto
|
||||
):
|
||||
continue
|
||||
if not any(
|
||||
addr.get(field) != value
|
||||
for field, value in ip_range_data.items()
|
||||
if field in addr
|
||||
):
|
||||
return
|
||||
updated_addresses = list(current_addresses)
|
||||
updated_addresses[index] = {**addr, **ip_range_data}
|
||||
self.network_object_group_update_addresses(updated_addresses)
|
||||
return
|
||||
|
||||
self.network_object_group_update_addresses(
|
||||
list(current_addresses) + [ip_range_data]
|
||||
)
|
||||
|
||||
def ip_range_remove(self):
|
||||
current_addresses = self.object_group_info.get('addresses') or []
|
||||
ip_addr = self.aparams['ip_addr_range']['start']
|
||||
ip_proto = self.aparams['ip_proto']
|
||||
|
||||
addresses_to_keep = [
|
||||
addr for addr in current_addresses
|
||||
if addr.get('ip_addr') != ip_addr
|
||||
or (
|
||||
ip_proto is not None
|
||||
and addr.get('net_address_type') != ip_proto
|
||||
)
|
||||
]
|
||||
if len(addresses_to_keep) == len(current_addresses):
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='ip_range',
|
||||
id=ip_addr,
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
self.network_object_group_update_addresses(addresses_to_keep)
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj='ip_range',
|
||||
id=ip_addr,
|
||||
)
|
||||
)
|
||||
|
||||
@DecortController.waypoint
|
||||
def network_object_group_get(self) -> dict[str, Any]:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/get',
|
||||
arg_params={'object_group_id': self.object_group_id},
|
||||
not_fail_codes=[404],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='network object group',
|
||||
id=self.object_group_id,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def network_object_group_update_addresses(self, addresses: list):
|
||||
payload = self.group_payload()
|
||||
payload['addresses'] = addresses
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.put,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/update',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self._object_group_info = response.json()
|
||||
self.set_changed()
|
||||
|
||||
def run(self):
|
||||
self.object_group_id = self.aparams['object_group_id']
|
||||
|
||||
if self.aparams['state'] == 'present':
|
||||
self.ip_range_add()
|
||||
else:
|
||||
self.ip_range_remove()
|
||||
|
||||
self.facts = self.find_ip_range() or {}
|
||||
self.exit()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNNetworkObjectGroupIPRange().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
245
library/decort_sdn_network_object_group_logical_port.py
Normal file
245
library/decort_sdn_network_object_group_logical_port.py
Normal file
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_network_object_group_logical_port
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNNetworkObjectGroupLogicalPort(DecortController):
|
||||
object_group_id: str
|
||||
_object_group_info: dict[str, Any] | None = None
|
||||
object_group_version: int | None = None
|
||||
logical_port_id: str
|
||||
logical_port_version: int | None = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
access_group_id=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
object_group_id=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
logical_port_id=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
logical_port_version=dict(
|
||||
type='int',
|
||||
),
|
||||
version_id=dict(
|
||||
type='int',
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=['present', 'absent'],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def object_group_info(self) -> dict[str, Any]:
|
||||
if self._object_group_info is None:
|
||||
if not isinstance(self.object_group_id, str):
|
||||
raise TypeError
|
||||
info = self.network_object_group_get()
|
||||
if info is None:
|
||||
raise TypeError
|
||||
self._object_group_info = info
|
||||
return self._object_group_info
|
||||
|
||||
def run(self):
|
||||
self.object_group_id = self.aparams['object_group_id']
|
||||
self.logical_port_id = self.aparams['logical_port_id']
|
||||
|
||||
is_attached = self.is_logical_port_attached()
|
||||
state = self.aparams['state']
|
||||
self.object_group_version = self.get_object_group_version()
|
||||
self.logical_port_version = self.get_port_version(
|
||||
is_attached=is_attached
|
||||
)
|
||||
|
||||
if state == 'present':
|
||||
if not is_attached:
|
||||
self.network_object_group_logical_port_attach()
|
||||
elif state == 'absent':
|
||||
if is_attached:
|
||||
self.network_object_group_logical_port_detach()
|
||||
|
||||
self.facts = {}
|
||||
self.exit()
|
||||
|
||||
def get_object_group_version(self) -> int:
|
||||
provided_version: int | None = self.aparams['version_id']
|
||||
current_version: int = self.object_group_info['version_id']
|
||||
|
||||
if (
|
||||
provided_version is not None
|
||||
and provided_version != current_version
|
||||
):
|
||||
self.message(
|
||||
'Check for parameters "version_id" failed: '
|
||||
'object group version mismatch: '
|
||||
f'given version: {provided_version}, '
|
||||
f'current version: {current_version}.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
return provided_version or current_version
|
||||
|
||||
@DecortController.waypoint
|
||||
def network_object_group_get(self) -> dict[str, Any] | None:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/get',
|
||||
arg_params={'object_group_id': self.object_group_id},
|
||||
not_fail_codes=[404],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='network object group',
|
||||
id=self.object_group_id,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
def logical_port_get(self, logical_port_id: str) -> dict[str, Any]:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/logical_port/get',
|
||||
arg_params={'logical_port_id': logical_port_id},
|
||||
not_fail_codes=[404],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='logical port',
|
||||
id=logical_port_id,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return response.json()
|
||||
|
||||
def is_logical_port_attached(self) -> bool:
|
||||
logical_ports = self.object_group_info.get('logical_ports') or []
|
||||
for lp in logical_ports:
|
||||
if lp['id'] == self.logical_port_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_port_version_from_object_group(self) -> int | None:
|
||||
logical_ports = self.object_group_info.get('logical_ports') or []
|
||||
for lp in logical_ports:
|
||||
if lp['id'] != self.logical_port_id:
|
||||
continue
|
||||
return lp['version_id']
|
||||
return None
|
||||
|
||||
def get_port_version(self, is_attached: bool) -> int:
|
||||
provided_version = self.aparams['logical_port_version']
|
||||
|
||||
current_version = None
|
||||
if is_attached:
|
||||
current_version = self.get_port_version_from_object_group()
|
||||
|
||||
if current_version is None:
|
||||
info = self.logical_port_get(logical_port_id=self.logical_port_id)
|
||||
current_version = info['version_id']
|
||||
|
||||
if (
|
||||
provided_version is not None
|
||||
and provided_version != current_version
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "logical_port_version" failed: '
|
||||
'logical port version mismatch: '
|
||||
f'given version: {provided_version}, '
|
||||
f'current version: {current_version}.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
return provided_version or current_version
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def network_object_group_logical_port_attach(self):
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.post,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/attach_logical_ports', # noqa: E501
|
||||
arg_json_body={
|
||||
'object_group_id': self.object_group_id,
|
||||
'access_group_id': self.aparams['access_group_id'],
|
||||
'version_id': self.object_group_version,
|
||||
'port_bindings': [
|
||||
{
|
||||
'port_id': self.logical_port_id,
|
||||
'port_version': self.logical_port_version,
|
||||
}
|
||||
],
|
||||
},
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.message(
|
||||
msg=(
|
||||
f'Logical port ID {self.logical_port_id} attached to object '
|
||||
f'group ID {self.object_group_id}.'
|
||||
)
|
||||
)
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def network_object_group_logical_port_detach(self):
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.post,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/detach_logical_ports', # noqa: E501
|
||||
arg_json_body={
|
||||
'object_group_id': self.object_group_id,
|
||||
'access_group_id': self.aparams['access_group_id'],
|
||||
'version_id': self.object_group_version,
|
||||
'port_bindings': [
|
||||
{
|
||||
'port_id': self.logical_port_id,
|
||||
'port_version': self.logical_port_version,
|
||||
}
|
||||
],
|
||||
},
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.message(
|
||||
msg=(
|
||||
f'Logical port ID {self.logical_port_id} detached from object '
|
||||
f'group ID {self.object_group_id}.'
|
||||
)
|
||||
)
|
||||
self.set_changed()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNNetworkObjectGroupLogicalPort().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
166
library/decort_sdn_network_object_group_mac.py
Normal file
166
library/decort_sdn_network_object_group_mac.py
Normal file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_network_object_group_mac
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNNetworkObjectGroupMAC(DecortController):
|
||||
network_object_group_id: str | None = None
|
||||
_network_object_group_info: dict[str, Any] | None = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
network_object_group_id=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
mac=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
choices=['present', 'absent'],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def network_network_object_group_info(self) -> dict[str, Any]:
|
||||
if self._network_object_group_info is None:
|
||||
network_network_object_group_info = self.network_object_group_get()
|
||||
if network_network_object_group_info is None:
|
||||
raise TypeError
|
||||
self._network_object_group_info = network_network_object_group_info
|
||||
return self._network_object_group_info
|
||||
|
||||
def find_mac(self) -> dict | None:
|
||||
mac = self.aparams['mac']
|
||||
for addr in self.network_network_object_group_info.get('addresses') or []: # noqa: E501
|
||||
if addr.get('mac_addr') == mac:
|
||||
return addr
|
||||
return None
|
||||
|
||||
def group_payload(self) -> dict:
|
||||
return {
|
||||
'object_group_id': self.network_object_group_id,
|
||||
'version_id': self.network_network_object_group_info['version_id'],
|
||||
'access_group_id': self.network_network_object_group_info['access_group_id'], # noqa: E501
|
||||
'description': self.network_network_object_group_info['description'], # noqa: E501
|
||||
'name': self.network_network_object_group_info['name'],
|
||||
}
|
||||
|
||||
def mac_add(self):
|
||||
current_addresses = self.network_network_object_group_info.get('addresses') or [] # noqa: E501
|
||||
mac = self.aparams['mac']
|
||||
|
||||
for addr in current_addresses:
|
||||
if addr.get('mac_addr') == mac:
|
||||
return
|
||||
|
||||
mac_data = {
|
||||
'mac_addr': mac,
|
||||
'net_address_type': 'MAC',
|
||||
}
|
||||
self.network_object_group_update_addresses(
|
||||
list(current_addresses) + [mac_data]
|
||||
)
|
||||
|
||||
def mac_remove(self):
|
||||
current_addresses = self.network_network_object_group_info.get('addresses') or [] # noqa: E501
|
||||
mac = self.aparams['mac']
|
||||
|
||||
addresses_to_keep = [
|
||||
addr for addr in current_addresses
|
||||
if addr.get('mac_addr') != mac
|
||||
]
|
||||
if len(addresses_to_keep) == len(current_addresses):
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='mac',
|
||||
id=mac,
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
self.network_object_group_update_addresses(addresses_to_keep)
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj='mac',
|
||||
id=mac,
|
||||
)
|
||||
)
|
||||
|
||||
@DecortController.waypoint
|
||||
def network_object_group_get(self) -> dict[str, Any]:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/get',
|
||||
arg_params={'object_group_id': self.network_object_group_id},
|
||||
not_fail_codes=[404],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='network object group',
|
||||
id=self.network_object_group_id,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def network_object_group_update_addresses(self, addresses: list):
|
||||
payload = self.group_payload()
|
||||
payload['addresses'] = addresses
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.put,
|
||||
arg_api_name='/restmachine/sdn/network_object_group/update',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self._network_object_group_info = response.json()
|
||||
self.set_changed()
|
||||
|
||||
def package_facts(self) -> dict:
|
||||
facts = self.find_mac() or {}
|
||||
if 'mac_addr' in facts:
|
||||
facts['mac'] = facts.pop('mac_addr')
|
||||
return facts
|
||||
|
||||
def run(self):
|
||||
self.network_object_group_id = self.aparams['network_object_group_id']
|
||||
|
||||
if self.aparams['state'] == 'present':
|
||||
self.mac_add()
|
||||
else:
|
||||
self.mac_remove()
|
||||
|
||||
self.facts = self.package_facts()
|
||||
self.exit()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNNetworkObjectGroupMAC().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
357
library/decort_sdn_segment.py
Normal file
357
library/decort_sdn_segment.py
Normal file
@@ -0,0 +1,357 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_segment
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNSegment(DecortController):
|
||||
REQUIRED_FIELDS = (
|
||||
'access_group_id',
|
||||
'description',
|
||||
'dhcp_v4',
|
||||
'dhcp_v6',
|
||||
'display_name',
|
||||
'subnet_v4',
|
||||
'subnet_v6',
|
||||
'type',
|
||||
)
|
||||
|
||||
segment_id: str | None = None
|
||||
_segment_info: dict[str, Any] | None = None
|
||||
need_final_get: bool = True
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
access_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
),
|
||||
dhcp_v4=dict(
|
||||
type='dict',
|
||||
),
|
||||
dhcp_v6=dict(
|
||||
type='dict',
|
||||
),
|
||||
display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
segment_id=dict(
|
||||
type='str',
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'present',
|
||||
'enabled',
|
||||
'disabled',
|
||||
'absent',
|
||||
'absent_force',
|
||||
],
|
||||
),
|
||||
subnet_v4=dict(
|
||||
type='str',
|
||||
),
|
||||
subnet_v6=dict(
|
||||
type='str',
|
||||
),
|
||||
type=dict(
|
||||
type='str',
|
||||
choices=['User', 'ExtNet'],
|
||||
),
|
||||
version_id=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def segment_info(self) -> dict[str, Any]:
|
||||
if self._segment_info is None:
|
||||
if not isinstance(self.segment_id, str):
|
||||
raise TypeError
|
||||
segment_info = self.segment_get()
|
||||
if segment_info is None:
|
||||
raise TypeError
|
||||
self._segment_info = segment_info
|
||||
return self._segment_info
|
||||
|
||||
def run(self):
|
||||
state = self.aparams['state']
|
||||
|
||||
if self.aparams['segment_id']:
|
||||
self.segment_id = self.aparams['segment_id']
|
||||
self._segment_info = self.segment_get(fail_if_not_found=False)
|
||||
elif self.aparams['display_name']:
|
||||
segment_info = self.segment_find(
|
||||
display_name=self.aparams['display_name']
|
||||
)
|
||||
self._segment_info = segment_info
|
||||
if segment_info:
|
||||
self.segment_id = segment_info['id']
|
||||
|
||||
if state in ('absent', 'absent_force'):
|
||||
if self._segment_info is None:
|
||||
self.exit()
|
||||
if self.segment_id:
|
||||
self.segment_delete(
|
||||
force=state == 'absent_force',
|
||||
)
|
||||
else:
|
||||
self.need_final_get = False
|
||||
elif state == 'present':
|
||||
if self.segment_id:
|
||||
self.check_amodule_args_for_update()
|
||||
self.segment_update()
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
self.segment_create()
|
||||
elif state in ('enabled', 'disabled'):
|
||||
if not self.segment_id:
|
||||
self.message(
|
||||
'Check for parameter "state" failed: state values '
|
||||
'"enabled"/"disabled" can only be applied to an existing '
|
||||
'segment.'
|
||||
)
|
||||
self.check_amodule_args_for_update()
|
||||
self.segment_update(
|
||||
desired_enabled=(state == 'enabled'),
|
||||
)
|
||||
|
||||
if self.need_final_get:
|
||||
self.facts = self.segment_get()
|
||||
self.exit()
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
|
||||
if (
|
||||
self.aparams['subnet_v4'] is None
|
||||
and self.aparams['subnet_v6'] is None
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameters "subnet_v4/subnet_v6" failed: at '
|
||||
'least one of these parameters must be specified when '
|
||||
'creating a segment.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_update(self):
|
||||
check_errors = False
|
||||
|
||||
if (
|
||||
self.aparams['version_id'] is not None
|
||||
and self.aparams['version_id'] != self.segment_info['version_id']
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameters "version_id" failed: '
|
||||
'segment version mismatch: '
|
||||
f'given version: {self.aparams['version_id']}, '
|
||||
f'current version: {self.segment_info['version_id']}.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def segment_get(
|
||||
self,
|
||||
access_group_id: str | None = None,
|
||||
fail_if_not_found=True,
|
||||
) -> dict[str, Any] | None:
|
||||
params = {'segment_id': self.segment_id}
|
||||
if access_group_id is not None:
|
||||
params['access_group_id'] = access_group_id
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/segment/get',
|
||||
arg_params=params,
|
||||
not_fail_codes=[404],
|
||||
accept_json_response=True,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
if fail_if_not_found:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='segment',
|
||||
id=self.segment_id,
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
else:
|
||||
return None
|
||||
return response.json()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def segment_find(self, display_name: str) -> dict[str, Any] | None:
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/segment/list',
|
||||
arg_params={
|
||||
'display_name': display_name,
|
||||
},
|
||||
accept_json_response=True,
|
||||
)
|
||||
return response.json()[0] if response.json() else None
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def segment_create(self):
|
||||
payload = dict()
|
||||
for field in self.REQUIRED_FIELDS:
|
||||
value = self.aparams[field]
|
||||
if value is not None:
|
||||
payload[field] = value
|
||||
payload['enabled'] = True
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.post,
|
||||
arg_api_name='/restmachine/sdn/segment/create',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self._segment_info = response.json()
|
||||
self.segment_id = response.json()['id']
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def segment_update(
|
||||
self,
|
||||
desired_enabled: bool | None = None,
|
||||
):
|
||||
need_update = False
|
||||
|
||||
for field in self.REQUIRED_FIELDS:
|
||||
value = self.aparams[field]
|
||||
if value is None:
|
||||
continue
|
||||
current_value = self.segment_info.get(field)
|
||||
if isinstance(value, dict) and isinstance(current_value, dict):
|
||||
if any(
|
||||
current_value.get(key) != expected_value
|
||||
for key, expected_value in value.items()
|
||||
):
|
||||
need_update = True
|
||||
break
|
||||
continue
|
||||
if value != current_value:
|
||||
need_update = True
|
||||
break
|
||||
|
||||
if (
|
||||
not need_update
|
||||
and desired_enabled is not None
|
||||
and desired_enabled != self.segment_info['enabled']
|
||||
):
|
||||
need_update = True
|
||||
|
||||
if need_update:
|
||||
payload = {
|
||||
'segment_id': self.segment_id,
|
||||
'version_id': (
|
||||
self.aparams['version_id']
|
||||
or self.segment_info['version_id']
|
||||
),
|
||||
'access_group_id': (
|
||||
self.aparams['access_group_id']
|
||||
or self.segment_info['access_group_id']
|
||||
),
|
||||
'description': (
|
||||
self.aparams['description']
|
||||
or self.segment_info['description']
|
||||
),
|
||||
'dhcp_v4': (
|
||||
self.aparams['dhcp_v4'] or self.segment_info.get('dhcp_v4')
|
||||
),
|
||||
'dhcp_v6': (
|
||||
self.aparams['dhcp_v6'] or self.segment_info.get('dhcp_v6')
|
||||
),
|
||||
'display_name': (
|
||||
self.aparams['display_name']
|
||||
or self.segment_info['display_name']
|
||||
),
|
||||
'enabled': (
|
||||
desired_enabled
|
||||
if desired_enabled is not None
|
||||
else self.segment_info['enabled']
|
||||
),
|
||||
'subnet_v4': (
|
||||
self.aparams['subnet_v4']
|
||||
or self.segment_info.get('subnet_v4')
|
||||
),
|
||||
'subnet_v6': (
|
||||
self.aparams['subnet_v6']
|
||||
or self.segment_info.get('subnet_v6')
|
||||
),
|
||||
'type': (
|
||||
self.aparams['type'] or self.segment_info['type']
|
||||
),
|
||||
}
|
||||
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.put,
|
||||
arg_api_name='/restmachine/sdn/segment/update',
|
||||
arg_json_body=payload,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.set_changed()
|
||||
|
||||
@DecortController.waypoint
|
||||
@DecortController.checkmode
|
||||
def segment_delete(
|
||||
self,
|
||||
force: bool = False,
|
||||
):
|
||||
version_id = self.aparams['version_id']
|
||||
if version_id is None:
|
||||
version_id = self.segment_info['version_id']
|
||||
payload = {
|
||||
'segment_id': self.segment_id,
|
||||
'version_id': version_id,
|
||||
}
|
||||
self.decort_api_call(
|
||||
arg_req_function=requests.delete,
|
||||
arg_api_name='/restmachine/sdn/segment/delete',
|
||||
arg_params=payload,
|
||||
)
|
||||
self.need_final_get = False
|
||||
self.set_changed()
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj='segment',
|
||||
id=self.segment_id,
|
||||
permanently=force,
|
||||
)
|
||||
)
|
||||
self.facts = {}
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNSegment().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
142
library/decort_sdn_segment_list.py
Normal file
142
library/decort_sdn_segment_list.py
Normal file
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_sdn_segment_list
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import requests
|
||||
|
||||
|
||||
class DecortSDNSegmentList(DecortController):
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
filter=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
enabled=dict(
|
||||
type='bool',
|
||||
),
|
||||
is_synced=dict(
|
||||
type='bool',
|
||||
),
|
||||
display_name=dict(
|
||||
type='str',
|
||||
),
|
||||
subnet=dict(
|
||||
type='str',
|
||||
),
|
||||
access_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
created_from=dict(
|
||||
type='str',
|
||||
),
|
||||
created_to=dict(
|
||||
type='str',
|
||||
),
|
||||
updated_from=dict(
|
||||
type='str',
|
||||
),
|
||||
updated_to=dict(
|
||||
type='str',
|
||||
),
|
||||
operation_status=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'Idle',
|
||||
'SynchronizingAtCore',
|
||||
'SynchronizingAtOVN',
|
||||
'Synchronized',
|
||||
'NoHypervisorAtOVN',
|
||||
'FailedAtCore',
|
||||
'TemporaryFailedAtCore',
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
pagination=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
number=dict(
|
||||
type='int',
|
||||
default=1,
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
),
|
||||
sorting=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
asc=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'display_name',
|
||||
'subnet',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
],
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.get_info()
|
||||
self.exit()
|
||||
|
||||
def get_info(self):
|
||||
api_params: dict[str, Any] = dict()
|
||||
|
||||
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||
for field, value in aparam_filter.items():
|
||||
if value is not None:
|
||||
api_params[field] = value
|
||||
|
||||
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||
api_params['page'] = aparam_pagination['number']
|
||||
api_params['per_page'] = aparam_pagination['size']
|
||||
|
||||
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
|
||||
if aparam_sorting:
|
||||
api_params['sort_by'] = aparam_sorting['field']
|
||||
api_params['sort_order'] = (
|
||||
'asc' if aparam_sorting['asc'] else 'desc'
|
||||
)
|
||||
|
||||
response = self.decort_api_call(
|
||||
arg_req_function=requests.get,
|
||||
arg_api_name='/restmachine/sdn/segment/list',
|
||||
arg_params=api_params,
|
||||
accept_json_response=True,
|
||||
)
|
||||
self.facts = response.json()
|
||||
|
||||
|
||||
def main():
|
||||
DecortSDNSegmentList().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -136,7 +136,7 @@ class DecortSecurityGroup(DecortController):
|
||||
)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
if (
|
||||
e.orig_exception.response
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
|
||||
@@ -42,7 +42,7 @@ class DecortStoragePolicy(DecortController):
|
||||
)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
if (
|
||||
e.orig_exception.response
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
|
||||
@@ -42,7 +42,7 @@ class DecortTrunk(DecortController):
|
||||
)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
if (
|
||||
e.orig_exception.response
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
|
||||
@@ -10,6 +10,8 @@ description: See L(Module Documentation,https://repository.basistech.ru/BASIS/de
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
|
||||
from dynamix_sdk import exceptions as sdk_exceptions
|
||||
|
||||
|
||||
class DecortUser(DecortController):
|
||||
def __init__(self):
|
||||
@@ -43,16 +45,35 @@ class DecortUser(DecortController):
|
||||
self.facts = self.usermanager_whoami_result
|
||||
self.id = self.facts['name']
|
||||
|
||||
user_get = self.user_get(id=self.id)
|
||||
for key in ['emailaddresses', 'data']:
|
||||
self.facts[key] = user_get[key]
|
||||
try:
|
||||
user_model = self.api.cloudapi.user.get(user_name=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='user',
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
raise e
|
||||
|
||||
self.facts.update(user_model.model_dump())
|
||||
|
||||
if self.aparams['resource_consumption']:
|
||||
self.facts.update(self.user_resource_consumption())
|
||||
self.facts.update(
|
||||
self.api.ca.user.get_resource_consumption().model_dump()
|
||||
)
|
||||
|
||||
# Delete duplicate self.facts['name']
|
||||
del self.facts['user_name']
|
||||
|
||||
if self.aparams['api_methods']:
|
||||
self.facts['api_methods'] = self.user_api_methods(id=self.id)
|
||||
|
||||
self.facts['api_methods'] = (
|
||||
self.api.cloudapi.user.api_list(user_name=self.id).model_dump()
|
||||
)
|
||||
search_string = self.aparams['objects_search']
|
||||
if search_string:
|
||||
self.facts['objects_search'] = self.user_objects_search(
|
||||
|
||||
@@ -20,7 +20,6 @@ class decort_vins(DecortController):
|
||||
|
||||
self.vins_id = 0
|
||||
self.vins_level = "" # "ID" if specified by ID, "RG" - at resource group, "ACC" - at account level
|
||||
vins_facts = None # will hold ViNS facts
|
||||
validated_rg_id = 0
|
||||
rg_facts = None # will hold RG facts
|
||||
validated_acc_id = 0
|
||||
@@ -28,15 +27,28 @@ class decort_vins(DecortController):
|
||||
if arg_amodule.params['vins_id']:
|
||||
# expect existing ViNS with the specified ID
|
||||
# This call to vins_find will abort the module if no ViNS with such ID is present
|
||||
self.vins_id, self.vins_facts = self.vins_find(arg_amodule.params['vins_id'],check_state=False)
|
||||
self.vins_id, self._vins_info = self.vins_find(
|
||||
arg_amodule.params['vins_id'],
|
||||
check_state=False,
|
||||
)
|
||||
if self.vins_id == 0:
|
||||
if arg_amodule.params['state'] == 'absent':
|
||||
self.exit()
|
||||
else:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='VINS',
|
||||
id=arg_amodule.params['vins_id'],
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
if self._vins_info is None:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Specified ViNS ID {} not found.".format(arg_amodule.params['vins_id'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.vins_level = "ID"
|
||||
#raise Exception(self.vins_facts)
|
||||
validated_acc_id = self.vins_facts['accountId']
|
||||
validated_rg_id = self.vins_facts['rgId']
|
||||
validated_acc_id = self._vins_info.account_id
|
||||
validated_rg_id = self._vins_info.rg_id
|
||||
|
||||
elif arg_amodule.params['rg_id']:
|
||||
# expect ViNS @ RG level in the RG with specified ID
|
||||
@@ -44,14 +56,16 @@ class decort_vins(DecortController):
|
||||
# This call to rg_find will abort the module if no RG with such ID is present
|
||||
validated_rg_id, rg_facts = self.rg_find(0, # account ID set to 0 as we search for RG by RG ID
|
||||
arg_amodule.params['rg_id'], arg_rg_name="")
|
||||
validated_acc_id = rg_facts['accountId']
|
||||
validated_acc_id = rg_facts.account_id
|
||||
|
||||
# This call to vins_find may return vins_id=0 if no ViNS found
|
||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
||||
self.vins_id, self._vins_info = self.vins_find(
|
||||
vins_id=0,
|
||||
vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=0,
|
||||
rg_id=arg_amodule.params['rg_id'],
|
||||
rg_facts=rg_facts,
|
||||
check_state=False)
|
||||
check_state=False,
|
||||
)
|
||||
# TODO: add checks and setup ViNS presence flags accordingly
|
||||
pass
|
||||
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
|
||||
@@ -66,27 +80,39 @@ class decort_vins(DecortController):
|
||||
# expect ViNS @ RG level in the RG with specified name under specified account
|
||||
# RG with the specified name must be present under the account, otherwise abort the module
|
||||
validated_rg_id, rg_facts = self.rg_find(validated_acc_id, 0, arg_amodule.params['rg_name'])
|
||||
if (not validated_rg_id or
|
||||
rg_facts['status'] in ["DESTROYING", "DESTROYED", "DELETING", "DELETED", "DISABLING", "ENABLING"]):
|
||||
if (not validated_rg_id or rg_facts is None or
|
||||
rg_facts.status in [
|
||||
sdk_types.ResourceGroupStatus.DESTROYING,
|
||||
sdk_types.ResourceGroupStatus.DESTROYED,
|
||||
sdk_types.ResourceGroupStatus.DELETED,
|
||||
sdk_types.ResourceGroupStatus.DISABLING,
|
||||
sdk_types.ResourceGroupStatus.ENABLING,
|
||||
]
|
||||
):
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "RG name '{}' not found or has invalid state.".format(arg_amodule.params['rg_name'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
# This call to vins_find may return vins_id=0 if no ViNS with this name found under specified RG
|
||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=0, # set to 0, as we are looking for ViNS under RG
|
||||
# (account_id) set to 0, as we are looking for ViNS under RG
|
||||
self.vins_id, self._vins_info = self.vins_find(
|
||||
vins_id=0,
|
||||
vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=0,
|
||||
rg_id=validated_rg_id,
|
||||
rg_facts=rg_facts,
|
||||
check_state=False)
|
||||
check_state=False,
|
||||
)
|
||||
self.vins_level = "RG"
|
||||
# TODO: add checks and setup ViNS presence flags accordingly
|
||||
else: # At this point we know for sure that rg_name="" and rg_id=0
|
||||
# So we expect ViNS @ account level
|
||||
# This call to vins_find may return vins_id=0 if no ViNS found
|
||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
||||
self.vins_id, self._vins_info = self.vins_find(
|
||||
vins_id=0,
|
||||
vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=validated_acc_id,
|
||||
rg_id=0,
|
||||
rg_facts=rg_facts,
|
||||
check_state=False)
|
||||
check_state=False,
|
||||
)
|
||||
self.vins_level = "ACC"
|
||||
# TODO: add checks and setup ViNS presence flags accordingly
|
||||
else:
|
||||
@@ -106,7 +132,10 @@ class decort_vins(DecortController):
|
||||
self.rg_id = validated_rg_id
|
||||
self.acc_id = validated_acc_id
|
||||
|
||||
if self.vins_id and self.vins_facts['status'] != 'DESTROYED':
|
||||
if (
|
||||
self._vins_info
|
||||
and self._vins_info.status != sdk_types.VINSStatus.DESTROYED
|
||||
):
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
@@ -114,65 +143,110 @@ class decort_vins(DecortController):
|
||||
return
|
||||
|
||||
def create(self):
|
||||
self.vins_id = self.vins_provision(self.amodule.params['vins_name'],
|
||||
self.acc_id, self.rg_id,
|
||||
self.amodule.params['ipcidr'],
|
||||
self.amodule.params['ext_net_id'], self.amodule.params['ext_ip_addr'],
|
||||
self.amodule.params['description'],
|
||||
zone_id=self.amodule.params['zone_id'],
|
||||
security_group_mode = self.amodule.params['security_group_mode']
|
||||
if security_group_mode is None:
|
||||
security_group_mode = False
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='security_group_mode',
|
||||
default_value=security_group_mode
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
|
||||
if self.amodule.params['mgmtaddr'] or self.amodule.params['connect_to']:
|
||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
||||
self.vins_id = self.vins_provision(
|
||||
vins_name=self.amodule.params['vins_name'],
|
||||
account_id=self.acc_id,
|
||||
rg_id=self.rg_id,
|
||||
ipcidr=self.amodule.params['ipcidr'],
|
||||
ext_net_id=self.amodule.params['ext_net_id'],
|
||||
ext_ip_addr=self.amodule.params['ext_ip_addr'],
|
||||
desc=self.amodule.params['description'],
|
||||
zone_id=self.amodule.params['zone_id'],
|
||||
security_group_mode=security_group_mode,
|
||||
)
|
||||
if self.vins_id:
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
if self.amodule.params['connect_to']:
|
||||
self.vins_update_ifaces(self.vins_facts,self.amodule.params['connect_to'],)
|
||||
self.vins_update_ifaces(
|
||||
self.vins_info,
|
||||
self.amodule.params['connect_to'],
|
||||
)
|
||||
if self.amodule.params['mgmtaddr']:
|
||||
self.vins_update_mgmt(self.vins_facts,self.amodule.params['mgmtaddr'])
|
||||
|
||||
self.vins_update_mgmt(
|
||||
self.vins_info,
|
||||
self.amodule.params['mgmtaddr'],
|
||||
)
|
||||
return
|
||||
def action(self,d_state='',restore=False):
|
||||
if restore == True:
|
||||
self.vins_restore(arg_vins_id=self.vins_id)
|
||||
self.vins_state(self.vins_facts, 'enabled')
|
||||
self.vins_facts['status'] = "ENABLED"
|
||||
self.vins_facts['VNFDev']['techStatus'] = "STARTED"
|
||||
|
||||
self.vins_update_extnet(self.vins_facts,
|
||||
def action(self, d_state='', restore=False):
|
||||
if restore:
|
||||
self.sdk_checkmode(self.api.cloudapi.vins.restore)(
|
||||
vins_id=self.vins_info.id,
|
||||
)
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
self.vins_state(self.vins_info, 'enabled')
|
||||
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
|
||||
if (
|
||||
self.amodule.params['ext_net_id'] is not None
|
||||
or self.amodule.params['ext_ip_addr'] is not None
|
||||
):
|
||||
self.vins_update_extnet(
|
||||
self.vins_info,
|
||||
self.amodule.params['ext_net_id'],
|
||||
self.amodule.params['ext_ip_addr'],
|
||||
)
|
||||
|
||||
if d_state == 'enabled' and self.vins_facts['status'] == "DISABLED":
|
||||
self.vins_state(self.vins_facts, d_state)
|
||||
self.vins_facts['status'] = "ENABLED"
|
||||
self.vins_facts['VNFDev']['techStatus'] = "STARTED"
|
||||
if (
|
||||
d_state == 'enabled'
|
||||
and self.vins_info.status == sdk_types.VINSStatus.DISABLED
|
||||
):
|
||||
self.vins_state(self.vins_info, d_state)
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
d_state = ''
|
||||
|
||||
if self.vins_facts['status'] == "ENABLED" and self.vins_facts['VNFDev']['techStatus'] == "STARTED":
|
||||
self.vins_update_ifaces(self.vins_facts,
|
||||
if (
|
||||
self.vins_info.status == sdk_types.VINSStatus.ENABLED
|
||||
and self.vins_info.vnfdev.tech_status == (
|
||||
sdk_types.VNFDevTechStatus.STARTED
|
||||
)
|
||||
):
|
||||
self.vins_update_ifaces(
|
||||
self.vins_info,
|
||||
self.amodule.params['connect_to'],
|
||||
)
|
||||
if self.result['changed']:
|
||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
||||
self.vins_update_mgmt(self.vins_facts,
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
|
||||
self.vins_update_mgmt(
|
||||
self.vins_info,
|
||||
self.amodule.params['mgmtaddr'],
|
||||
)
|
||||
|
||||
if d_state != '':
|
||||
self.vins_state(self.vins_facts, d_state)
|
||||
self.vins_state(self.vins_info, d_state)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.vins_facts['zoneId']:
|
||||
if (
|
||||
aparam_zone_id is not None
|
||||
and aparam_zone_id != self.vins_info.zone_id
|
||||
):
|
||||
self.vins_migrate_to_zone(
|
||||
net_id=self.vins_id,
|
||||
net_id=self.vins_info.id,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
def delete(self):
|
||||
self.vins_delete(self.vins_id, self.amodule.params['permanently'])
|
||||
self.vins_facts['status'] = 'DESTROYED'
|
||||
self.sdk_checkmode(self.api.cloudapi.vins.delete)(
|
||||
vins_id=self.vins_info.id,
|
||||
permanently=self.amodule.params['permanently'],
|
||||
)
|
||||
return
|
||||
|
||||
def nop(self):
|
||||
"""No operation (NOP) handler for ViNS management by decort_vins module.
|
||||
This function is intended to be called from the main switch construct of the module
|
||||
@@ -181,23 +255,27 @@ class decort_vins(DecortController):
|
||||
"""
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = False
|
||||
if self.vins_id:
|
||||
self.result['msg'] = ("No state change required for ViNS ID {} because of its "
|
||||
"current status '{}'.").format(self.vins_id, self.vins_facts['status'])
|
||||
if self._vins_info:
|
||||
self.result['msg'] = (
|
||||
f'No state change required for ViNS ID {self._vins_info.id} '
|
||||
f'because of its "current status "{self._vins_info.status}".'
|
||||
)
|
||||
else:
|
||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
||||
"non-existent ViNS instance.").format(self.amodule.params['state'])
|
||||
return
|
||||
|
||||
def error(self):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
if self.vins_id:
|
||||
if self._vins_info:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for ViNS ID {} in the "
|
||||
"current status '{}'").format(self.vins_id,
|
||||
self.amodule.params['state'],
|
||||
self.vins_facts['status'])
|
||||
self.result['msg'] = (
|
||||
f'Invalid target state "{self.amodule.params['state']}" '
|
||||
f'requested for ViNS ID {self._vins_info.id} in the '
|
||||
f'current status "{self._vins_info.status}"'
|
||||
)
|
||||
else:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
@@ -205,6 +283,7 @@ class decort_vins(DecortController):
|
||||
"ViNS name '{}'").format(self.amodule.params['state'],
|
||||
self.amodule.params['vins_name'])
|
||||
return
|
||||
|
||||
def package_facts(self, arg_check_mode=False):
|
||||
"""Package a dictionary of ViNS facts according to the decort_vins module specification.
|
||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||
@@ -222,40 +301,12 @@ class decort_vins(DecortController):
|
||||
# in check mode return immediately with the default values
|
||||
return ret_dict
|
||||
|
||||
if self.vins_facts is None:
|
||||
if self._vins_info is None:
|
||||
# if void facts provided - change state value to ABSENT and return
|
||||
ret_dict['state'] = "ABSENT"
|
||||
return ret_dict
|
||||
|
||||
ret_dict['id'] = self.vins_facts['id']
|
||||
ret_dict['name'] = self.vins_facts['name']
|
||||
ret_dict['state'] = self.vins_facts['status']
|
||||
ret_dict['account_id'] = self.vins_facts['accountId']
|
||||
ret_dict['rg_id'] = self.vins_facts['rgId']
|
||||
ret_dict['int_net_addr'] = self.vins_facts['network']
|
||||
ret_dict['gid'] = self.vins_facts['gid']
|
||||
custom_interfaces = list(filter(lambda i: i['type']=="CUSTOM",self.vins_facts['VNFDev']['interfaces']))
|
||||
if custom_interfaces:
|
||||
ret_dict['custom_net_addr'] = []
|
||||
for runner in custom_interfaces:
|
||||
ret_dict['custom_net_addr'].append(runner['ipAddress'])
|
||||
mgmt_interfaces = list(filter(lambda i: i['listenSsh'] and i['name']!="ens9",self.vins_facts['VNFDev']['interfaces']))
|
||||
if mgmt_interfaces:
|
||||
ret_dict['ssh_ipaddr'] = []
|
||||
for runner in mgmt_interfaces:
|
||||
ret_dict['ssh_ipaddr'].append(runner['ipAddress'])
|
||||
ret_dict['ssh_password'] = self.vins_facts['VNFDev']['config']['mgmt']['password']
|
||||
ret_dict['ssh_port'] = 9022
|
||||
if self.vins_facts['vnfs'].get('GW'):
|
||||
gw_config = self.vins_facts['vnfs']['GW']['config']
|
||||
ret_dict['ext_ip_addr'] = gw_config['ext_net_ip']
|
||||
ret_dict['ext_net_id'] = gw_config['ext_net_id']
|
||||
else:
|
||||
ret_dict['ext_ip_addr'] = ""
|
||||
ret_dict['ext_net_id'] = -1
|
||||
ret_dict['zone_id'] = self.vins_facts['zoneId']
|
||||
|
||||
return ret_dict
|
||||
return self._vins_info.model_dump()
|
||||
|
||||
|
||||
@property
|
||||
@@ -276,11 +327,9 @@ class decort_vins(DecortController):
|
||||
),
|
||||
ext_net_id=dict(
|
||||
type='int',
|
||||
default=-1,
|
||||
),
|
||||
ext_ip_addr=dict(
|
||||
type='str',
|
||||
default='',
|
||||
),
|
||||
ipcidr=dict(
|
||||
type='str',
|
||||
@@ -304,7 +353,6 @@ class decort_vins(DecortController):
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
default='present',
|
||||
choices=[
|
||||
'absent',
|
||||
'disabled',
|
||||
@@ -335,6 +383,9 @@ class decort_vins(DecortController):
|
||||
zone_id=dict(
|
||||
type=int,
|
||||
),
|
||||
security_group_mode=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
@@ -347,6 +398,34 @@ class decort_vins(DecortController):
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if (
|
||||
self.amodule.params['ext_ip_addr']
|
||||
and self.amodule.params['ext_net_id'] is None
|
||||
and self.vins_info.vnfs.gw is None
|
||||
):
|
||||
self.message(
|
||||
msg=(
|
||||
'Check for parameter "ext_net_id" failed: '
|
||||
'the "ext_net_id" parameter must be specified '
|
||||
'if the "ext_ip_addr" parameter is passed and '
|
||||
'VINS is not connected to an external network.'
|
||||
)
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
if (
|
||||
self.aparams['security_group_mode'] is not None
|
||||
and self.vins_info.security_group_mode != self.aparams['security_group_mode']
|
||||
):
|
||||
self.message(
|
||||
msg=(
|
||||
'Check for parameter "security_group_mode" failed: '
|
||||
'"security_group_mode" cannot be changed '
|
||||
'for existing ViNS'
|
||||
)
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@@ -384,36 +463,54 @@ class decort_vins(DecortController):
|
||||
# if cconfig_save is true, only config save without other updates
|
||||
vins_should_exist = False
|
||||
|
||||
if self.vins_id:
|
||||
if self._vins_info:
|
||||
vins_should_exist = True
|
||||
if self.vins_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING"]:
|
||||
if self._vins_info.status in [
|
||||
sdk_types.VINSStatus.MODELED,
|
||||
sdk_types.VINSStatus.DISABLING,
|
||||
sdk_types.VINSStatus.ENABLING,
|
||||
sdk_types.VINSStatus.DELETING,
|
||||
sdk_types.VINSStatus.DESTROYING,
|
||||
]:
|
||||
# error: nothing can be done to existing ViNS in the listed statii regardless of
|
||||
# the requested state
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("No change can be done for existing ViNS ID {} because of its current "
|
||||
"status '{}'").format(self.vins_id, self.vins_facts['status'])
|
||||
elif self.vins_facts['status'] == "DISABLED":
|
||||
self.result['msg'] = (
|
||||
f'No change can be done for existing '
|
||||
f'ViNS ID {self.vins_id} because of its '
|
||||
f'current status {self._vins_info.status}'
|
||||
)
|
||||
elif self._vins_info.status == sdk_types.VINSStatus.DISABLED:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.delete()
|
||||
vins_should_exist = False
|
||||
elif amodule.params['state'] in ('present', 'disabled'):
|
||||
elif (
|
||||
amodule.params['state'] is None
|
||||
or amodule.params['state'] in ('present', 'disabled')
|
||||
):
|
||||
# update ViNS, leave in disabled state
|
||||
self.action()
|
||||
elif amodule.params['state'] == 'enabled':
|
||||
# update ViNS and enable
|
||||
self.action('enabled')
|
||||
elif self.vins_facts['status'] in ["CREATED", "ENABLED"]:
|
||||
elif self._vins_info.status in [
|
||||
sdk_types.VINSStatus.CREATED,
|
||||
sdk_types.VINSStatus.ENABLED,
|
||||
]:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.delete()
|
||||
vins_should_exist = False
|
||||
elif amodule.params['state'] in ('present', 'enabled'):
|
||||
elif (
|
||||
amodule.params['state'] is None
|
||||
or amodule.params['state'] in ('present', 'enabled')
|
||||
):
|
||||
# update ViNS
|
||||
self.action()
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
# disable and update ViNS
|
||||
self.action('disabled')
|
||||
elif self.vins_facts['status'] == "DELETED":
|
||||
elif self._vins_info.status == sdk_types.VINSStatus.DELETED:
|
||||
if amodule.params['state'] in ['present', 'enabled']:
|
||||
# restore and enable
|
||||
self.action(restore=True)
|
||||
@@ -426,28 +523,48 @@ class decort_vins(DecortController):
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
self.error()
|
||||
vins_should_exist = False
|
||||
elif self.vins_facts['status'] == "DESTROYED":
|
||||
if amodule.params['state'] in ('present', 'enabled'):
|
||||
elif self._vins_info.status == sdk_types.VINSStatus.DESTROYED:
|
||||
state = amodule.params['state']
|
||||
if state is None:
|
||||
state = 'present'
|
||||
self.message(
|
||||
msg=(
|
||||
f'State not specified, '
|
||||
f'default value "{state}" will be used.'
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
if state in ('present', 'enabled'):
|
||||
# need to re-provision ViNS;
|
||||
self.create()
|
||||
vins_should_exist = True
|
||||
elif amodule.params['state'] == 'absent':
|
||||
elif state == 'absent':
|
||||
self.nop()
|
||||
vins_should_exist = False
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
elif state == 'disabled':
|
||||
self.error()
|
||||
else:
|
||||
state = amodule.params['state']
|
||||
if state is None:
|
||||
state = 'present'
|
||||
self.message(
|
||||
msg=(
|
||||
f'State not specified, '
|
||||
f'default value "{state}" will be used.'
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
# Preexisting ViNS was not found.
|
||||
vins_should_exist = False # we will change it back to True if ViNS is created or restored
|
||||
# If requested state is 'absent' - nothing to do
|
||||
if amodule.params['state'] == 'absent':
|
||||
if state == 'absent':
|
||||
self.nop()
|
||||
elif amodule.params['state'] in ('present', 'enabled'):
|
||||
elif state in ('present', 'enabled'):
|
||||
self.check_amodule_argument('vins_name')
|
||||
# as we already have account ID and RG ID we can create ViNS and get vins_id on success
|
||||
self.create()
|
||||
vins_should_exist = True
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
elif state == 'disabled':
|
||||
self.error()
|
||||
#
|
||||
# conditional switch end - complete module run
|
||||
@@ -457,7 +574,7 @@ class decort_vins(DecortController):
|
||||
else:
|
||||
# prepare ViNS facts to be returned as part of self.result and then call exit_json(...)
|
||||
if self.result['changed']:
|
||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
self.result['facts'] = self.package_facts(amodule.check_mode)
|
||||
amodule.exit_json(**self.result)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class decort_vm(DecortController):
|
||||
|
||||
validated_acc_id = 0
|
||||
validated_rg_id = 0
|
||||
validated_rg_facts = None
|
||||
validated_rg_model = None
|
||||
|
||||
self.vm_to_clone_id = 0
|
||||
self.vm_to_clone_info = None
|
||||
@@ -118,21 +118,21 @@ class decort_vm(DecortController):
|
||||
self.fail_json(**self.result)
|
||||
# fail the module -> exit
|
||||
# now validate RG
|
||||
validated_rg_id, validated_rg_facts = self.rg_find(validated_acc_id,
|
||||
validated_rg_id, validated_rg_model = self.rg_find(validated_acc_id,
|
||||
arg_amodule.params['rg_id'],
|
||||
arg_amodule.params['rg_name'])
|
||||
if not validated_rg_id:
|
||||
if not validated_rg_id or not validated_rg_model:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||
arg_amodule.params['rg_name'])
|
||||
self.fail_json(**self.result)
|
||||
self.amodule.fail_json(**self.result)
|
||||
# fail the module - exit
|
||||
|
||||
self.rg_id = validated_rg_id
|
||||
arg_amodule.params['rg_id'] = validated_rg_id
|
||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
||||
self.acc_id = validated_rg_facts['accountId']
|
||||
arg_amodule.params['rg_name'] = validated_rg_model.name
|
||||
self.acc_id = validated_rg_model.account_id
|
||||
|
||||
# at this point we are ready to locate Compute, and if anything fails now, then it must be
|
||||
# because this Compute does not exist or something goes wrong in the upstream API
|
||||
@@ -635,7 +635,11 @@ class decort_vm(DecortController):
|
||||
"""Compute destroy handler for VM management by decort_vm module.
|
||||
Note that this handler deletes the VM permanently together with all assigned disk resources.
|
||||
"""
|
||||
self.compute_delete(comp_id=self.comp_id, permanently=True)
|
||||
self.sdk_checkmode(self.api.ca.compute.delete)(
|
||||
vm_id=self.comp_id,
|
||||
detach_disks=True,
|
||||
permanently=True,
|
||||
)
|
||||
self.comp_id, self.comp_info, _ = self._compute_get_by_id(self.comp_id)
|
||||
return
|
||||
|
||||
@@ -693,7 +697,13 @@ class decort_vm(DecortController):
|
||||
aparam_disk_id = aparam_boot['disk_id']
|
||||
if aparam_disk_id is not None:
|
||||
for disk in self.comp_info['disks']:
|
||||
if disk['id'] == aparam_disk_id and disk['type'] != 'B':
|
||||
if (
|
||||
disk['id'] == aparam_disk_id
|
||||
and not self.is_vm_boot_disk(
|
||||
vm_chipset=self.comp_info['chipset'],
|
||||
vm_disk=disk,
|
||||
)
|
||||
):
|
||||
self.compute_boot_disk(
|
||||
comp_id=self.comp_info['id'],
|
||||
boot_disk=aparam_disk_id,
|
||||
@@ -1000,7 +1010,10 @@ class decort_vm(DecortController):
|
||||
|
||||
ret_dict['disks'] = self.comp_info['disks']
|
||||
for disk in ret_dict['disks']:
|
||||
if disk['type'] == 'B':
|
||||
if self.is_vm_boot_disk(
|
||||
vm_chipset=self.comp_info['chipset'],
|
||||
vm_disk=disk,
|
||||
):
|
||||
# if it is a boot disk - store its size
|
||||
ret_dict['disk_size'] = disk['sizeMax']
|
||||
|
||||
@@ -1073,6 +1086,8 @@ class decort_vm(DecortController):
|
||||
|
||||
ret_dict['read_only'] = self.comp_info['read_only']
|
||||
|
||||
ret_dict['weight'] = self.comp_info['weight']
|
||||
|
||||
return ret_dict
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
@@ -1199,7 +1214,7 @@ class decort_vm(DecortController):
|
||||
)
|
||||
elif (
|
||||
aparam_storage_policy_id
|
||||
not in self.rg_info['storage_policy_ids']
|
||||
not in self.rg_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -1681,7 +1696,9 @@ class decort_vm(DecortController):
|
||||
if new_boot_disk_size is not None:
|
||||
boot_disk_size = 0
|
||||
for disk in self.comp_info['disks']:
|
||||
if disk['type'] == 'B':
|
||||
if self.is_vm_boot_disk(
|
||||
vm_chipset=self.comp_info['chipset'], vm_disk=disk,
|
||||
):
|
||||
boot_disk_size = disk['sizeMax']
|
||||
break
|
||||
else:
|
||||
@@ -1844,7 +1861,9 @@ class decort_vm(DecortController):
|
||||
aparam_disks_ids = [disk['id'] for disk in aparam_disks]
|
||||
comp_boot_disk_id = None
|
||||
for comp_disk in self.comp_info['disks']:
|
||||
if comp_disk['type'] == 'B':
|
||||
if self.is_vm_boot_disk(
|
||||
vm_chipset=self.comp_info['chipset'], vm_disk=comp_disk,
|
||||
):
|
||||
comp_boot_disk_id = comp_disk['id']
|
||||
break
|
||||
disks_to_detach = []
|
||||
@@ -2137,8 +2156,8 @@ class decort_vm(DecortController):
|
||||
vm_has_shared_sep_disk = False
|
||||
vm_disk_ids = [disk['id'] for disk in self.comp_info['disks']]
|
||||
for disk_id in vm_disk_ids:
|
||||
_, disk_info = self._disk_get_by_id(disk_id=disk_id)
|
||||
if disk_info['sepType'] == 'SHARED':
|
||||
disk_info = self._disk_get_by_id(disk_id=disk_id)
|
||||
if disk_info.sep_type == sdk_types.SEPType.SHARED:
|
||||
vm_has_shared_sep_disk = True
|
||||
break
|
||||
|
||||
@@ -2230,7 +2249,9 @@ class decort_vm(DecortController):
|
||||
if disk_redeploy:
|
||||
vm_has_boot_disk = False
|
||||
for disk in self.comp_info['disks']:
|
||||
if disk['type'] == 'B':
|
||||
if self.is_vm_boot_disk(
|
||||
vm_chipset=self.comp_info['chipset'], vm_disk=disk,
|
||||
):
|
||||
vm_has_boot_disk = True
|
||||
break
|
||||
if not vm_has_boot_disk:
|
||||
@@ -2341,7 +2362,9 @@ class decort_vm(DecortController):
|
||||
check_errors = False
|
||||
|
||||
# check if account has vm feature “trunk”
|
||||
if not self.check_account_vm_features(vm_feature=self.VMFeature.trunk):
|
||||
if not self.check_account_vm_features(
|
||||
vm_feature=sdk_types.VMFeature.TRUNK,
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
@@ -2349,7 +2372,7 @@ class decort_vm(DecortController):
|
||||
'trunk type networks '
|
||||
)
|
||||
# check if rg has vm feature “trunk”
|
||||
if not self.check_rg_vm_features(vm_feature=self.VMFeature.trunk):
|
||||
if not self.check_rg_vm_features(vm_feature=sdk_types.VMFeature.TRUNK):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
|
||||
@@ -40,7 +40,7 @@ class DecortZone(DecortController):
|
||||
zone_model = self.api.cloudapi.zone.get(id=self.id)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
if (
|
||||
e.orig_exception.response
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
||||
ansible==11.6.0
|
||||
requests==2.32.3
|
||||
git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@1.4.latest
|
||||
git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@1.5.latest
|
||||
|
||||
Reference in New Issue
Block a user