3 Commits

Author SHA1 Message Date
e54a9591e4 11.0.0 2026-02-11 13:50:28 +03:00
8c554c8edd 10.0.1 2025-12-02 15:31:36 +03:00
becbe65993 10.0.0 2025-11-14 12:44:02 +03:00
45 changed files with 7716 additions and 5826 deletions

View File

@@ -1,30 +0,0 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: end-of-file-fixer
- id: no-commit-to-branch
name: no-commit-to-branch (main, master, dev_*)
args:
- --pattern
- dev_.*
- repo: https://github.com/pycqa/flake8
rev: 7.2.0
hooks:
- id: flake8
exclude: |
(?x)^(
module_utils/decort_utils.py |
library/decort_bservice.py |
library/decort_disk.py |
library/decort_group.py |
library/decort_k8s.py |
library/decort_kvmvm.py |
library/decort_lb.py |
library/decort_osimage.py |
library/decort_pfw.py |
library/decort_rg.py |
library/decort_vins.py
)$
args:
- --extend-ignore=E402

View File

@@ -1,122 +1,170 @@
# Список изменений в версии 9.0.0 # Список изменений в версии 11.0.0
## Добавлено ## Добавлено
### Глобально ### Глобально
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-798 | Обновлены системные требования: версия интерпретатора Python обновлена до 3.12, версия Python-библиотеки ansible обновлена до 11.6.0 | | BANS-909 | В системные требования добавлена библиотека Python `dynamix_sdk`. |
| BANS-918 | Добавлен общий для всех модулей параметр `ignore_api_compatibility`. |
| BANS-913 | Добавлен общий для всех модулей параметр `ignore_sdk_version_check`. |
| BANS-954 | Добавлен модуль `decort_vm` в связи с переименованием из `decort_kvmvm`. |
| BANS-953 | Добавлен модуль `decort_image` в связи с переименованием из `decort_osimage`. |
| BANS-997 | Добавлен модуль `decort_security_group_list`, позволяющий получить список доступных групп безопасности. |
| BANS-884 | Добавлен модуль `decort_disk_list`, позволяющий получить список доступных дисков. |
| BANS-936 | Добавлен модуль `decort_rg_list`, позволяющий получить список доступных ресурсных групп. |
| BANS-949 | Добавлен модуль `decort_vins_list`, позволяющий получить список доступных внутренних сетей. |
| BANS-940 | Добавлен модуль `decort_vm_list`, позволяющий получить список доступных виртуальных машин. |
| BANS-959 | Добавлен модуль `decort_flip_group_list`, позволяющий получить список доступных групп с плавающим IP-адресом. |
| BANS-952 | Добавлен модуль `decort_image_list`, позволяющий получить список доступных образов. |
| BANS-983 | Добавлен модуль `decort_account_list`, позволяющий получить список доступных аккаунтов. |
| BANS-985 | Добавлен модуль `decort_audit_list`, позволяющий получить список аудитов. |
| BANS-988 | Добавлен модуль `decort_trunk_list`, позволяющий получить список доступных транковых портов. |
| BANS-987 | Добавлен модуль `decort_zone_list`, позволяющий получить список доступных зон. |
| BANS-989 | Добавлен модуль `decort_storage_policy_list`, позволяющий получить список политик хранения. |
| BANS-945 | Добавлен модуль `decort_user` в связи с переименованием из `decort_user_info`. |
### Модуль decort_kvmvm ### Модуль decort_vm
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-790 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. | | BANS-926 | Для параметра `chipset` добавлено значение по умолчанию `Q35` при создании ВМ. |
| BANS-810 | Добавлен параметр `guest_agent` и возвращаемое значение `guest_agent`. | | BANS-933 | Добавлено возвращаемое значение `pinned_to_node` в связи с переименованием из `pinned_to_stack`. |
| BANS-806 | Добавлен параметр `get_snapshot_merge_status` и возвращаемое значение `snapshot_merge_status`. | | BANS-934 | Добавлено возвращаемое значение `read_only`. |
| BANS-823 | Добавлено значение `TRUNK` для параметра `networks.type`. | | BANS-994 | Добавлена возможность задать параметр `mtu` при создании сетевого интерфейса для TRUNK-сети и изменить `mtu` у существующего интерфейса, подключённого к TRUNK-сети. |
| BANS-813 | Добавлено значение `SDN` для параметра `networks.type`. | | BANS-991 | Добавлена возможность указать параметр `ip_addr` при присоединении и изменении `DPDK` сети. |
| BANS-835 | Добавлена возможность использования параметра `networks.mtu` для внешней сети. | | BANS-1017 | Добавлено возвращаемое значение `disks.cache`. |
| BANS-1034 | Добавлена возможность указать параметр `ip_addr` при присоединении и изменении `VFNIC` сети. |
| BANS-992 | Добавлен параметр `networks.net_prefix`. |
### Модуль decort_lb ### Модуль decort_group
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-793 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. | | BANS-927 | Для параметра `chipset` добавлено значение по умолчанию `Q35` при создании группы. |
| BANS-819 | Добавлено возвращаемое значение `account_id`. |
| BANS-800 | Добавлены значения `stopped` и `started` для параметра `state` и возвращаемое значение `tech_status`. |
### Модуль decort_k8s ### Модуль decort_k8s
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-794 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. | | BANS-928 | Для параметра `chipset` добавлено значение по умолчанию `Q35` при создании кластера. |
| BANS-804 | Добавлены значения `stopped` и `started` для параметра `state`. |
### Модуль decort_vins
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-791 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. |
### Модуль decort_bservice
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-792 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. |
| BANS-805 | Добавлены значения `stopped` и `started` для параметра `state`. |
### Модуль decort_user_info
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-796 | Добавлен параметр `zones` и возвращаемое значение `zones`. |
| BANS-826 | Добавлен параметр `trunks` и возвращаемое значение `trunks`. |
### Модуль decort_account ### Модуль decort_account
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-789 | Добавлен параметр `default_zone_id` и возвращаемые значение `zoneIds`, `defaultZoneId`. | | BANS-966 | Добавлен параметр `get_resource_consumption` и возвращаемое значение `resource_consumption`. |
### Модуль decort_account_info
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-809 | Добавлено значение `MERGE` для параметра `computes.filter.tech_status`. |
| BANS-855 | Добавлены значения `SNAPCREATE`, `CLONING`, `ROLLBACK` для параметра `computes.filter.tech_status`. |
### Модуль decort_rg
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-812 | Добавлен параметр `sdn_access_group_id` и возвращаемое значение `sdn_access_group_id`. |
### Модуль decort_zone
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-795 | Добавлен модуль `decort_zone` для получения информации о зонах. |
### Модуль decort_trunk ### Модуль decort_trunk
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-825 | Добавлен модуль `decort_trunk` для получения информации о транковых портах. | | BANS-993 | Добавлено возвращаемое значение `mtu`. |
| BANS-976 | Добавлены возвращаемые значения `created_datetime`, `deleted_datetime`, `updated_datetime`. |
### Модуль decort_snapshot ### Модуль decort_zone
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-808 | Добавлено значение `merge_aborted` для параметра `state`. | | BANS-970 | Добавлены возвращаемые значения `created_datetime`, `updated_datetime` и возвращаемые значения `account_ids`, `bservice_ids`, `vm_ids`, `extnet_ids`, `k8s_ids`, `lb_ids`, `vins_ids` в связи с переименованием из `accountIds`, `bserviceIds`, `computeIds`, `extnetIds`, `k8sIds`, `lbIds`, `vinsIds`. |
| BANS-1024 | Добавлено возвращаемое значение `node_auto_start`. |
### Модуль decort_osimage ### Модуль decort_vm_snapshot
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-849 | Добавлен параметр `account_id`, используемый при создании шаблонных и виртуальных образов. | | BANS-978 | Добавлено возвращаемое значение `datetime`. |
### Модуль decort_storage_policy
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-977 | Добавлены возвращаемые значения `sep_name`, `sep_tech_status`. |
## Удалено
### Модуль decort_disk ### Модуль decort_disk
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-815 | Удалено значение по умолчанию для параметра `description`. | | BANS-1019 | Добавлено возвращаемое значение `cache_mode`. |
| BANS-1050 | Добавлено возвращаемое значение `blkdiscard`. |
### Модуль decort_lb ## Удалено
### Глобально
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-815 | Удалено значение по умолчанию для параметра `description`. | | BANS-954 | Удалён модуль `decort_kvmvm` в связи с переименованием в `decort_vm`. |
| BANS-969 | Модуль `decort_account_info` расформирован, его функционал перенесён в модули: `decort_disk_list`, `decort_rg_list`, `decort_vins_list`, `decort_vm_list`, `decort_flip_group_list`, `decort_image_list`, `decort_account`. |
### Модуль decort_k8s | BANS-953 | Удалён модуль `decort_osimage` в связи с переименованием в `decort_image`. |
| Идентификатор<br>задачи | Описание | | BANS-945 | Удалён модуль `decort_user_info` в связи с переименованием в `decort_user`. |
| --- | --- |
| BANS-804 | Удален параметр `started` в связи с переносом логики в параметр `state` (значение `started`). |
### Модуль decort_bservice
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-805 | Удален параметр `started` в связи с переносом логики в параметр `state` (значение `started`). |
### Модуль decort_osimage
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-849 | Удален параметр `account_Id` в связи с переименованием в `account_id`. |
## Исправлено
### Модуль decort_lb
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-803 | Модуль завершал работу ошибкой Python при создании балансировщика с указанием параметра `backends` или `frontends`. |
| BANS-820 | Выполнение модуля с указанием параметра `vins_id` и без указания параметра `ext_net_id` вызывало создание балансировщика с некорректной сетевой конфигурацией, дальнейшее добавление конфигурации backend к которому завершалось ошибкой платформы. |
| BANS-799 | Скорректирована логика параметра целевого состояния `present`. Теперь состояние `present` соответствует тому, что балансировщик нагрузки существует, и не приводит к изменению состояния существующего балансировщика нагрузки. Также для параметра `state` значение по умолчанию `present` теперь только при создании балансировщика нагрузки. |
### Модуль decort_account ### Модуль decort_account
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-817 | Модуль некорректно отслеживал завершение удаления и восстановления аккаунта. | | BANS-924 | Удалён параметр `quotas.ext_traffic`. |
| BANS-998 |Для параметра `state` удалено значение по умолчанию. |
### Модуль decort_rg
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-925 | Удалён параметр `quotas.net_transfer`. |
### Модуль decort_vm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-926 | Для параметра `chipset` удалено значение по умолчанию `i440fx` при создании ВМ. |
| BANS-933 | Удалено возвращаемое значение `pinned_to_stack` в связи с переименованием в `pinned_to_node`. |
| BANS-961 | Параметр `storage_policy_id` удалён из обязательных при пересоздании загрузочного диска. |
### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-927 | Для параметра `chipset` удалено значение по умолчанию `i440fx` при создании группы. |
| BANS-1027 | Удалён параметр `driver`. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-928 | Для параметра `chipset` удалено значение по умолчанию `i440fx` при создании кластера. |
### Модуль decort_user
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-983 | Удалён параметр `accounts` и возвращаемое значение `accounts` в связи с переносом этой функциональности в модуль `decort_account_list`. |
| BANS-985 | Удалён параметр `audits` и возвращаемое значение `audits` в связи с переносом этой функциональности в модуль `decort_audit_list`. |
| BANS-988 | Удалён параметр `trunks` и возвращаемое значение `trunks` в связи с переносом этой функциональности в модуль `decort_trunk_list`. |
| BANS-987 | Удалён параметр `zones` и возвращаемое значение `zones` в связи с переносом этой функциональности в модуль `decort_zone_list`. |
| BANS-989 | Удалён параметр `storage_policies` и возвращаемое значение `storage_policies` в связи с переносом этой функциональности в модуль `decort_storage_policy_list`. |
| BANS-997 | Удалён параметр `security_groups` и возвращаемое значение `security_groups` в связи с переносом этой функциональности в модуль `decort_security_group_list`. |
### Модуль decort_zone
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-970 | Удалены возвращаемые значения `accountIds`, `bserviceIds`, `computeIds`, `extnetIds`, `k8sIds`, `lbIds`, `vinsIds` в связи с переименованием в `account_ids`, `bservice_ids`, `vm_ids`, `extnet_ids`, `k8s_ids`, `lb_ids`, `vins_ids`. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1004 | Удалён параметр `reason` |
### Модуль decort_security_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1000 | Удалено возвращаемое значение `rules.remote_ip_prefix` в связи с переименованием в `rules.remote_net_cidr`. |
| BANS-1013 | Удален параметр `rules.objects.remote_ip_prefix` в связи с переименованием в `rules.objects.remote_net_cidr`. |
### Модуль decort_vm_snapshot
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1012 | Удалено возвращаемое значение `disks` в связи с переименованием в `disk_ids`. |
## Исправлено
### Модуль decort_vm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-996 | Параметры `mac`, `security_groups`, `enable_secgroups`, `enabled` сетевого интерфейса DPDK-сети могли меняться при изменении `mtu`. |
| BANS-1052 | Параметры `numa_affinity`, `cpu_pin`, `hp_backed` не применялись при создании ВМ без образа. |
### Модуль decort_bservice
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-389 | После создания базовой службы, модуль не возвращал информацию о созданном объекте. |
### Модуль decort_vm_snapshot
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1022 | После создания снимка не возвращалась информация о снимке. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-1033 | Модуль без необходимости выполнял запрос к API `/cloudapi/k8s/update`, передавая в него параметры, не вызывающие изменения. |

View File

@@ -1,3 +0,0 @@
dev:
pip install -r requirements-dev.txt
pre-commit install

View File

@@ -5,6 +5,8 @@
| Версия платформы | Версия модулей Ansible | | Версия платформы | Версия модулей Ansible |
|:----------------:|:--------------------------:| |:----------------:|:--------------------------:|
| 4.5.0 | 11.0.x |
| 4.4.0 | 10.0.x |
| 4.4.0 build 963 | 9.0.x | | 4.4.0 build 963 | 9.0.x |
| 4.3.0 | 8.0.x | | 4.3.0 | 8.0.x |
| 4.2.0 | 7.0.x, 7.1.x, 7.2.x | | 4.2.0 | 7.0.x, 7.1.x, 7.2.x |

View File

@@ -1,11 +1,11 @@
--- ---
# #
# DECORT osimage module example # DECORT image module example
# #
- hosts: localhost - hosts: localhost
tasks: tasks:
- name: create - name: create
decort_osimage: decort_image:
authenticator: oauth2 authenticator: oauth2
verify_ssl: False verify_ssl: False
controller_url: "https://ds1.digitalenergy.online" controller_url: "https://ds1.digitalenergy.online"

View File

@@ -1,14 +1,14 @@
--- ---
# #
# DECORT osimage module example # DECORT image module example
# #
- hosts: localhost - hosts: localhost
tasks: tasks:
- name: create_virtual_osimage - name: create_virtual_image
decort_osimage: decort_image:
authenticator: oauth2 authenticator: oauth2
controller_url: "https://ds1.digitalenergy.online" controller_url: "https://ds1.digitalenergy.online"
image_name: "alpine_linux_3.14.0" image_name: "alpine_linux_3.14.0"
virt_name: "alpine_last" virt_name: "alpine_last"
delegate_to: localhost delegate_to: localhost
register: osimage register: image

View File

@@ -1,11 +1,11 @@
--- ---
# #
# DECORT osimage module example # DECORT image module example
# #
- hosts: localhost - hosts: localhost
tasks: tasks:
- name: get_osimage - name: get_image
decort_osimage: decort_image:
authenticator: oauth2 authenticator: oauth2
controller_url: "https://ds1.digitalenergy.online" controller_url: "https://ds1.digitalenergy.online"
image_name: "alpine_linux_3.14.0" image_name: "alpine_linux_3.14.0"

View File

@@ -1,14 +1,14 @@
--- ---
# #
# DECORT osimage module example # DECORT image module example
# #
- hosts: localhost - hosts: localhost
tasks: tasks:
- name: rename_osimage - name: rename_image
decort_osimage: decort_image:
authenticator: oauth2 authenticator: oauth2
controller_url: "https://ds1.digitalenergy.online" controller_url: "https://ds1.digitalenergy.online"
image_name: "alpine_linux_3.14.0v2.0" image_name: "alpine_linux_3.14.0v2.0"
image_id: 54321 image_id: 54321
delegate_to: localhost delegate_to: localhost
register: osimage register: image

View File

@@ -62,6 +62,10 @@ class DecortAccount(DecortController):
name=dict( name=dict(
type='str', type='str',
), ),
get_resource_consumption=dict(
type='bool',
default=False,
),
quotas=dict( quotas=dict(
type='dict', type='dict',
options=dict( options=dict(
@@ -71,9 +75,6 @@ class DecortAccount(DecortController):
disks_size=dict( disks_size=dict(
type='int', type='int',
), ),
ext_traffic=dict(
type='int',
),
gpu=dict( gpu=dict(
type='int', type='int',
), ),
@@ -94,7 +95,6 @@ class DecortAccount(DecortController):
'disabled', 'disabled',
'present', 'present',
], ],
default='present',
), ),
sep_pools=dict( sep_pools=dict(
type='list', type='list',
@@ -131,7 +131,7 @@ class DecortAccount(DecortController):
""" """
arg_state = self.aparams['state'] arg_state = self.aparams['state']
if 'absent' in arg_state: if arg_state is not None and 'absent' in arg_state:
# Parameters or combinations of parameters that can # Parameters or combinations of parameters that can
# cause changing the object. # cause changing the object.
changing_params = [ changing_params = [
@@ -175,6 +175,7 @@ class DecortAccount(DecortController):
if check_error: if check_error:
self.exit(fail=True) self.exit(fail=True)
@DecortController.handle_sdk_exceptions
def run(self): def run(self):
self.get_info() self.get_info()
self.check_amodule_args_for_change() self.check_amodule_args_for_change()
@@ -187,6 +188,7 @@ class DecortAccount(DecortController):
self.acc_id, self._acc_info = self.account_find( self.acc_id, self._acc_info = self.account_find(
account_name=self.aparams['name'], account_name=self.aparams['name'],
account_id=self.aparams['id'], account_id=self.aparams['id'],
resource_consumption=self.aparams['get_resource_consumption'],
) )
# If this is a repeated getting info # If this is a repeated getting info
else: else:
@@ -195,6 +197,9 @@ class DecortAccount(DecortController):
if not self.amodule.check_mode: if not self.amodule.check_mode:
self.acc_id, self._acc_info = self.account_find( self.acc_id, self._acc_info = self.account_find(
account_id=self.acc_id, account_id=self.acc_id,
resource_consumption=(
self.aparams['get_resource_consumption']
),
) )
self.facts = self.acc_info self.facts = self.acc_info
@@ -361,7 +366,6 @@ class DecortAccount(DecortController):
quotas_naming = [ quotas_naming = [
['cpu', 'CU_C', 'cpu_quota'], ['cpu', 'CU_C', 'cpu_quota'],
['disks_size', 'CU_DM', 'disks_size_quota'], ['disks_size', 'CU_DM', 'disks_size_quota'],
['ext_traffic', 'CU_NP', 'ext_traffic_quota'],
['gpu', 'gpu_units', 'gpu_quota'], ['gpu', 'gpu_units', 'gpu_quota'],
['public_ip', 'CU_I', 'public_ip_quota'], ['public_ip', 'CU_I', 'public_ip_quota'],
['ram', 'CU_M', 'ram_quota'], ['ram', 'CU_M', 'ram_quota'],

View File

@@ -8,565 +8,46 @@ description: See L(Module Documentation,https://repository.basistech.ru/BASIS/de
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortAccountInfo(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(
audits=dict(
type='bool',
default=False
),
computes=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
ext_net_id=dict(
type='int',
),
ext_net_name=dict(
type='str'
),
id=dict(
type='int',
),
ip=dict(
type='str'
),
name=dict(
type='str'
),
rg_id=dict(
type='int',
),
rg_name=dict(
type='str'
),
tech_status=dict(
type='str',
choices=self.COMPUTE_TECH_STATUSES,
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_COMPUTE_LIST, # noqa: E501
required=True,
),
),
),
),
),
disks=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
id=dict(
type='int',
),
name=dict(
type='str',
),
size=dict(
type='int',
),
type=dict(
type='str',
choices=self.DISK_TYPES,
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_DISK_LIST, # noqa: E501
required=True,
),
),
),
),
),
flip_groups=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
ext_net_id=dict(
type='int',
),
id=dict(
type='int',
),
ip=dict(
type='str',
),
name=dict(
type='str',
),
vins_id=dict(
type='int',
),
vins_name=dict(
type='str',
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
),
),
id=dict(
type='int',
),
images=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
id=dict(
type='int',
),
name=dict(
type='str',
),
type=dict(
type='str',
choices=self.IMAGE_TYPES,
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_IMAGE_LIST, # noqa: E501
required=True,
),
),
),
),
),
name=dict(
type='str',
),
resource_groups=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
id=dict(
type='int',
),
name=dict(
type='str'
),
status=dict(
type='str',
choices=self.RESOURCE_GROUP_STATUSES,
),
vins_id=dict(
type='int'
),
vm_id=dict(
type='int'
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_RG_LIST, # noqa: E501
required=True,
),
),
),
),
),
resource_consumption=dict(
type='bool',
default=False
),
vinses=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
ext_ip=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str'
),
rg_id=dict(
type='int',
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=self.FIELDS_FOR_SORTING_ACCOUNT_VINS_LIST, # noqa: E501
required=True,
),
),
),
),
),
),
mutually_exclusive=[
('id', 'name')
],
required_one_of=[
('id', 'name')
],
supports_check_mode=True,
)
@property
def mapped_computes_args(self) -> None | dict:
"""
Map the module argument `computes` to
arguments dictionary for the method
`DecortController.account_computes`
(excluding for `account_id`).
"""
input_args = self.aparams['computes']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['compute_id'] = input_args['filter']['id']
mapped_args['compute_ip'] = input_args['filter']['ip']
mapped_args['compute_name'] = input_args['filter']['name']
mapped_args['compute_tech_status'] =\
input_args['filter']['tech_status']
mapped_args['ext_net_id'] = input_args['filter']['ext_net_id']
mapped_args['ext_net_name'] =\
input_args['filter']['ext_net_name']
mapped_args['rg_id'] = input_args['filter']['rg_id']
mapped_args['rg_name'] = input_args['filter']['rg_name']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
@property
def mapped_disks_args(self) -> None | dict:
"""
Map the module argument `disks` to
arguments dictionary for the method
`DecortController.account_disks`
(excluding for `account_id`).
"""
input_args = self.aparams['disks']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['disk_id'] = input_args['filter']['id']
mapped_args['disk_name'] = input_args['filter']['name']
mapped_args['disk_size'] = input_args['filter']['size']
mapped_args['disk_type'] = input_args['filter']['type']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
@property
def mapped_flip_groups_args(self) -> None | dict:
"""
Map the module argument `flip_groups` to
arguments dictionary for the method
`DecortController.account_flip_groups`
(excluding for `account_id`).
"""
input_args = self.aparams['flip_groups']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['ext_net_id'] = input_args['filter']['ext_net_id']
mapped_args['flig_group_id'] = input_args['filter']['id']
mapped_args['flig_group_ip'] = input_args['filter']['ip']
mapped_args['flig_group_name'] = input_args['filter']['name']
mapped_args['vins_id'] = input_args['filter']['vins_id']
mapped_args['vins_name'] = input_args['filter']['vins_name']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
return mapped_args
@property
def mapped_images_args(self) -> None | dict:
"""
Map the module argument `images` to
arguments dictionary for the method
`DecortController.account_images`
(excluding for `account_id`).
"""
input_args = self.aparams['images']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['image_id'] = input_args['filter']['id']
mapped_args['image_name'] = input_args['filter']['name']
mapped_args['image_type'] = input_args['filter']['type']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
@property
def mapped_rg_args(self) -> None | dict:
"""
Map the module argument `resource_groups` to
arguments dictionary for the method
`DecortController.account_resource_groups`
(excluding for `account_id`).
"""
input_args = self.aparams['resource_groups']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['rg_id'] =\
input_args['filter']['id']
mapped_args['rg_name'] =\
input_args['filter']['name']
mapped_args['rg_status'] =\
input_args['filter']['status']
mapped_args['vins_id'] =\
input_args['filter']['vins_id']
mapped_args['vm_id'] =\
input_args['filter']['vm_id']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
@property
def mapped_vinses_args(self) -> None | dict:
"""
Map the module argument `vinses` to
arguments dictionary for the method
`DecortController.account_vinses`
(excluding for `account_id`).
"""
input_args = self.aparams['vinses']
if not input_args:
return input_args
mapped_args = {}
if input_args['filter']:
mapped_args['vins_id'] = input_args['filter']['id']
mapped_args['vins_name'] = input_args['filter']['name']
mapped_args['ext_ip'] = input_args['filter']['ext_ip']
mapped_args['rg_id'] = input_args['filter']['rg_id']
if input_args['pagination']:
mapped_args['page_number'] =\
input_args['pagination']['number']
mapped_args['page_size'] =\
input_args['pagination']['size']
if input_args['sorting']:
mapped_args['sort_by_asc'] =\
input_args['sorting']['asc']
mapped_args['sort_by_field'] =\
input_args['sorting']['field']
return mapped_args
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.id, self.facts = self.account_find(
account_name=self.aparams['name'],
account_id=self.aparams['id'],
audits=self.aparams['audits'],
computes_args=self.mapped_computes_args,
disks_args=self.mapped_disks_args,
flip_groups_args=self.mapped_flip_groups_args,
images_args=self.mapped_images_args,
resource_consumption=self.aparams['resource_consumption'],
resource_groups_args=self.mapped_rg_args,
vinses_args=self.mapped_vinses_args,
fail_if_not_found=True,
)
def main(): def main():
DecortAccountInfo().run() module = AnsibleModule(
argument_spec=dict(
app_id=dict(type='raw'),
app_secret=dict(type='raw'),
authenticator=dict(type='raw'),
controller_url=dict(type='raw'),
domain=dict(type='raw'),
jwt=dict(type='raw'),
oauth2_url=dict(type='raw'),
password=dict(type='raw'),
username=dict(type='raw'),
verify_ssl=dict(type='raw'),
ignore_api_compatibility=dict(type='raw'),
ignore_sdk_version_check=dict(type='raw'),
audits=dict(type='raw'),
computes=dict(type='raw'),
disks=dict(type='raw'),
flip_groups=dict(type='raw'),
id=dict(type='raw'),
images=dict(type='raw'),
name=dict(type='raw'),
resource_groups=dict(type='raw'),
resource_consumption=dict(type='raw'),
vinses=dict(type='raw'),
),
)
module.fail_json(
msg=(
'The functionality of the module has been moved to the modules '
'"decort_disk_list", "decort_rg_list", "decort_vm_list", '
'"decort_vins_list", "decort_image_list", '
'"decort_flip_group_list", "decort_account".'
'\nPlease use the new modules to get information about the objects'
' available to the account.'
),
)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -0,0 +1,130 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_account_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortAccountList(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_type=dict(
type='str',
choices=sdk_types.AccessType._member_names_,
),
id=dict(
type='int',
),
name=dict(
type='str',
),
status=dict(
type='str',
choices=sdk_types.AccountStatus._member_names_,
),
zone_id=dict(
type='int',
),
),
),
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=(
sdk_types.AccountForCAAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_access_type: str | None = aparam_filter['access_type']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.AccountForCAAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.account.list(
access_type=(
sdk_types.AccessType[aparam_access_type]
if aparam_access_type else None
),
id=aparam_filter['id'],
name=aparam_filter['name'],
status=(
sdk_types.AccountStatus[aparam_status]
if aparam_status else None
),
zone_id=aparam_filter['zone_id'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortAccountList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,168 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_audit_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortAuditList(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(
account_id=dict(
type='int',
),
api_url_path=dict(
type='str',
),
bservice_id=dict(
type='int',
),
exclude_audit_lines=dict(
type='bool',
),
flip_group_id=dict(
type='int',
),
request_id=dict(
type='str',
),
k8s_id=dict(
type='int',
),
lb_id=dict(
type='int',
),
max_status_code=dict(
type='int',
),
min_status_code=dict(
type='int',
),
request_timestamp_end=dict(
type='int',
),
request_timestamp_start=dict(
type='int',
),
rg_id=dict(
type='int',
),
sep_id=dict(
type='int',
),
user_name=dict(
type='str',
),
vins_id=dict(
type='int',
),
vm_id=dict(
type='int',
),
),
),
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=(
sdk_types.AuditAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.AuditAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.audit.list(
account_id=aparam_filter['account_id'],
api_url_path=aparam_filter['api_url_path'],
bservice_id=aparam_filter['bservice_id'],
exclude_audit_lines=aparam_filter['exclude_audit_lines'] or False,
flip_group_id=aparam_filter['flip_group_id'],
request_id=aparam_filter['request_id'],
k8s_id=aparam_filter['k8s_id'],
lb_id=aparam_filter['lb_id'],
max_status_code=aparam_filter['max_status_code'],
min_status_code=aparam_filter['min_status_code'],
request_timestamp_end=aparam_filter['request_timestamp_end'],
request_timestamp_start=aparam_filter['request_timestamp_start'],
rg_id=aparam_filter['rg_id'],
sep_id=aparam_filter['sep_id'],
user_name=aparam_filter['user_name'],
vins_id=aparam_filter['vins_id'],
vm_id=aparam_filter['vm_id'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortAuditList().run()
if __name__ == '__main__':
main()

View File

@@ -28,7 +28,7 @@ class decort_bservice(DecortController):
self.fail_json(**self.result) self.fail_json(**self.result)
if not arg_amodule.params['id']: if not arg_amodule.params['id']:
if not arg_amodule.params['rg_id']: # RG ID is not set -> locate RG by name -> need account ID if not arg_amodule.params['rg_id']: # RG ID is not set -> locate RG by name -> need account ID
validated_acc_id, self.acc_info = self.account_find(arg_amodule.params['account_name'], validated_acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'],
arg_amodule.params['account_id']) arg_amodule.params['account_id'])
if not validated_acc_id: if not validated_acc_id:
self.result['failed'] = True self.result['failed'] = True
@@ -113,7 +113,9 @@ class decort_bservice(DecortController):
) )
if self.bservice_id: if self.bservice_id:
_, self.bservice_info = self.bservice_get_by_id(self.bservice_id) _, self.bservice_info = self.bservice_get_by_id(self.bservice_id)
self.bservice_state(self.bservice_info,'enabled') self.bservice_state(self.bservice_info, self.aparams['state'])
self.bservice_should_exist = True
return return
def action(self,d_state): def action(self,d_state):
@@ -176,7 +178,6 @@ class decort_bservice(DecortController):
), ),
state=dict( state=dict(
type='str', type='str',
default='present',
choices=[ choices=[
'absent', 'absent',
'disabled', 'disabled',
@@ -260,71 +261,80 @@ class decort_bservice(DecortController):
if check_errors: if check_errors:
self.exit(fail=True) self.exit(fail=True)
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
if self.amodule.check_mode:
self.result['changed'] = False
if self.bservice_id:
self.result['failed'] = False
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
# we exit the module at this point
else:
self.result['failed'] = True
self.result['msg'] = ("Cannot locate B-service name '{}'. Other arguments are: B-service ID {}, "
"RG name '{}', RG ID {}, Account '{}'.").format(amodule.params['name'],
amodule.params['id'],
amodule.params['rg_name'],
amodule.params['rg_id'],
amodule.params['account_name'])
amodule.fail_json(**self.result)
pass
#MAIN MANAGE PART
if self.bservice_id:
if self.bservice_info['status'] in ("DELETING","DESTROYNG","RECONFIGURING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED"):
self.error()
elif self.bservice_info['status'] == "DELETED":
if amodule.params['state'] in (
'disabled', 'enabled', 'present', 'started', 'stopped'
):
self.restore(self.bservice_id)
self.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
self.nop()
elif self.bservice_info['status'] in (
'ENABLED', 'DISABLED', 'CREATED',
):
if amodule.params['state'] == 'absent':
self.destroy()
else:
self.action(amodule.params['state'])
elif self.bservice_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'):
self.create()
self.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
self.nop()
else:
state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
self.nop()
if state in ('present','started'):
self.create()
elif state in ('stopped', 'disabled','enabled'):
self.error()
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.bservice_should_exist:
_, self.bservice_info = self.bservice_get_by_id(self.bservice_id)
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:
amodule.exit_json(**self.result)
def main(): def main():
subj = decort_bservice() decort_bservice().run()
amodule = subj.amodule
if subj.amodule.check_mode:
subj.result['changed'] = False
if subj.bservice_id:
subj.result['failed'] = False
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
# we exit the module at this point
else:
subj.result['failed'] = True
subj.result['msg'] = ("Cannot locate B-service name '{}'. Other arguments are: B-service ID {}, "
"RG name '{}', RG ID {}, Account '{}'.").format(amodule.params['name'],
amodule.params['id'],
amodule.params['rg_name'],
amodule.params['rg_id'],
amodule.params['account_name'])
amodule.fail_json(**subj.result)
pass
#MAIN MANAGE PART if __name__ == '__main__':
if subj.bservice_id:
if subj.bservice_info['status'] in ("DELETING","DESTROYNG","RECONFIGURING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED"):
subj.error()
elif subj.bservice_info['status'] == "DELETED":
if amodule.params['state'] in (
'disabled', 'enabled', 'present', 'started', 'stopped'
):
subj.restore(subj.bservice_id)
subj.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
subj.nop()
elif subj.bservice_info['status'] in ('ENABLED', 'DISABLED'):
if amodule.params['state'] == 'absent':
subj.destroy()
else:
subj.action(amodule.params['state'])
elif subj.bservice_info['status'] == "DESTROED":
if amodule.params['state'] in ('present','enabled'):
subj.create()
subj.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
subj.nop()
else:
if amodule.params['state'] == 'absent':
subj.nop()
if amodule.params['state'] in ('present','started'):
subj.create()
elif amodule.params['state'] in ('stopped', 'disabled','enabled'):
subj.error()
if subj.result['failed']:
amodule.fail_json(**subj.result)
else:
if subj.bservice_should_exist:
_, subj.bservice_info = subj.bservice_get_by_id(subj.bservice_id)
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
else:
amodule.exit_json(**subj.result)
if __name__ == "__main__":
main() main()

View File

@@ -41,6 +41,8 @@ class decort_disk(DecortController):
validated_acc_id, validated_acc_info = self.account_find( validated_acc_id, validated_acc_info = self.account_find(
arg_amodule.params['account_name'], arg_amodule.params['account_name'],
arg_amodule.params['account_id']) arg_amodule.params['account_id'])
self.acc_id = validated_acc_id
self._acc_info = validated_acc_info
if not validated_acc_id: if not validated_acc_id:
self.result['changed'] = False self.result['changed'] = False
self.result['msg'] = ( self.result['msg'] = (
@@ -51,8 +53,6 @@ class decort_disk(DecortController):
) )
self.amodule.fail_json(**self.result) self.amodule.fail_json(**self.result)
self.acc_id = validated_acc_id
self._acc_info = validated_acc_info
validated_disk_id, validated_disk_facts = self.disk_find( validated_disk_id, validated_disk_facts = self.disk_find(
disk_id=arg_amodule.params['id'], disk_id=arg_amodule.params['id'],
name=arg_amodule.params['name'] if "name" in arg_amodule.params else "", name=arg_amodule.params['name'] if "name" in arg_amodule.params else "",
@@ -67,70 +67,130 @@ class decort_disk(DecortController):
self.disk_id = validated_disk_id self.disk_id = validated_disk_id
self.disk_info = validated_disk_facts self.disk_info = validated_disk_facts
def create(self): if self.disk_id:
self.acc_id = validated_disk_facts['accountId']
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
self.disk_id = self.disk_create(accountId=self.acc_id, def compare_iotune_params(self, new_iotune: dict, current_iotune: dict):
name = self.amodule.params['name'], io_fields = sdk_types.IOTuneAPIResultNM.model_fields.keys()
description=self.amodule.params['description'],
size=self.amodule.params['size'], for field in io_fields:
iops=self.amodule.params['iops'], new_value = new_iotune.get(field)
sep_id=self.amodule.params['sep_id'], current_value = current_iotune.get(field)
pool=self.amodule.params['pool'],
if new_value != current_value:
return False
return True
def limit_io(self, aparam_limit_io: dict):
self.sdk_checkmode(self.api.cloudapi.disks.limit_io)(
disk_id=self.disk_id,
read_bytes_sec_max=(aparam_limit_io.get('read_bytes_sec_max')),
read_bytes_sec=(aparam_limit_io.get('read_bytes_sec')),
read_iops_sec_max=(aparam_limit_io.get('read_iops_sec_max')),
read_iops_sec=(aparam_limit_io.get('read_iops_sec')),
size_iops_sec=(aparam_limit_io.get('size_iops_sec')),
total_bytes_sec_max=(aparam_limit_io.get('total_bytes_sec_max')),
total_bytes_sec=(aparam_limit_io.get('total_bytes_sec')),
total_iops_sec_max=(aparam_limit_io.get('total_iops_sec_max')),
total_iops_sec=(aparam_limit_io.get('total_iops_sec')),
write_bytes_sec_max=(aparam_limit_io.get('write_bytes_sec_max')),
write_bytes_sec=(aparam_limit_io.get('write_bytes_sec')),
write_iops_sec_max=(aparam_limit_io.get('write_iops_sec_max')),
write_iops_sec=(aparam_limit_io.get('write_iops_sec')),
)
def create(self):
self.disk_id = self.sdk_checkmode(self.api.cloudapi.disks.create)(
account_id=self.acc_id,
name=self.amodule.params['name'],
size_gb=self.amodule.params['size'],
storage_policy_id=self.aparams['storage_policy_id'],
description=self.amodule.params['description'],
sep_id=self.amodule.params['sep_id'],
sep_pool_name=self.amodule.params['pool'],
) )
#IO tune #IO tune
if self.amodule.params['limitIO']: aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
self.disk_limitIO(disk_id=self.disk_id, self.limit_io(aparam_limit_io=aparam_limit_io)
limits=self.amodule.params['limitIO'])
#set share status #set share status
if self.amodule.params['shareable']: if self.amodule.params['shareable']:
self.disk_share(self.disk_id,self.amodule.params['shareable']) self.sdk_checkmode(self.api.cloudapi.disks.share)(
disk_id=self.disk_id,
)
return return
def action(self,restore=False): def action(self,restore=False):
#restore never be done #restore never be done
if restore: if restore:
self.disk_restore(self.disk_id) self.sdk_checkmode(self.api.cloudapi.disks.restore)(
disk_id=self.disk_id,
)
#rename if id present #rename if id present
if ( if (
self.amodule.params['name'] is not None 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.disk_rename(disk_id=self.disk_id, self.rename()
name=self.amodule.params['name'])
#resize #resize
if ( if (
self.amodule.params['size'] is not None self.amodule.params['size'] is not None
and self.amodule.params['size'] != self.disk_info['sizeMax'] and self.amodule.params['size'] != self.disk_info['sizeMax']
): ):
self.disk_resize(self.disk_info,self.amodule.params['size']) self.sdk_checkmode(self.api.cloudapi.disks.resize2)(
disk_id=self.disk_id,
disk_size_gb=self.amodule.params['size'],
)
#IO TUNE #IO TUNE
if self.amodule.params['limitIO']: aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
clean_io = [param for param in self.amodule.params['limitIO'] \ if aparam_limit_io:
if self.amodule.params['limitIO'][param] == None] if not self.compare_iotune_params(
for key in clean_io: del self.amodule.params['limitIO'][key] new_iotune=aparam_limit_io,
if self.amodule.params['limitIO'] != self.disk_info['iotune']: current_iotune=self.disk_info['iotune'],
self.disk_limitIO(self.disk_id,self.amodule.params['limitIO']) ):
self.limit_io(aparam_limit_io=aparam_limit_io)
#share check/update #share check/update
#raise Exception(self.amodule.params['shareable']) #raise Exception(self.amodule.params['shareable'])
if self.amodule.params['shareable'] != self.disk_info['shareable']: if self.amodule.params['shareable'] != self.disk_info['shareable']:
self.disk_share(self.disk_id,self.amodule.params['shareable']) if self.amodule.params['shareable']:
self.sdk_checkmode(self.api.cloudapi.disks.share)(
disk_id=self.disk_id,
)
else:
self.sdk_checkmode(self.api.cloudapi.disks.unshare)(
disk_id=self.disk_id,
)
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']
):
self.sdk_checkmode(self.api.ca.disks.change_disk_storage_policy)(
disk_id=self.disk_id,
storage_policy_id=aparam_storage_policy_id,
)
return return
def delete(self): def delete(self):
self.disk_id = self.disk_delete(disk_id=self.disk_id, self.sdk_checkmode(self.api.cloudapi.disks.delete)(
detach=self.amodule.params['force_detach'], disk_id=self.disk_id,
permanently=self.amodule.params['permanently'], detach=self.amodule.params['force_detach'],
reason=self.amodule.params['reason']) permanently=self.amodule.params['permanently'],
self.disk_info['status'] = "DELETED" )
self.disk_id, self.disk_info = self._disk_get_by_id(self.disk_id)
return return
def rename(self): def rename(self):
self.sdk_checkmode(self.api.cloudapi.disks.rename)(
disk_id=self.disk_id,
self.disk_rename(diskId = self.disk_id, name=self.amodule.params['name'],
name = self.amodule.params['name']) )
self.disk_info['name'] = self.amodule.params['name']
return return
def nop(self): def nop(self):
@@ -175,6 +235,10 @@ class decort_disk(DecortController):
ret_dict['iotune'] = self.disk_info['iotune'] ret_dict['iotune'] = self.disk_info['iotune']
ret_dict['size_available'] = self.disk_info['sizeAvailable'] ret_dict['size_available'] = self.disk_info['sizeAvailable']
ret_dict['size_used'] = self.disk_info['sizeUsed'] 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 ret_dict
@@ -219,12 +283,9 @@ class decort_disk(DecortController):
size=dict( size=dict(
type='int', type='int',
), ),
iops=dict(
type='int',
default=2000,
),
limitIO=dict( limitIO=dict(
type='dict', type='dict',
apply_defaults=True,
options=dict( options=dict(
total_bytes_sec=dict( total_bytes_sec=dict(
type='int', type='int',
@@ -275,10 +336,6 @@ class decort_disk(DecortController):
type='bool', type='bool',
default=False, default=False,
), ),
reason=dict(
type='str',
default='Managed by Ansible decort_disk',
),
state=dict( state=dict(
type='str', type='str',
default='present', default='present',
@@ -287,6 +344,9 @@ class decort_disk(DecortController):
'present', 'present',
], ],
), ),
storage_policy_id=dict(
type='int',
),
), ),
supports_check_mode=True, supports_check_mode=True,
required_one_of=[ required_one_of=[
@@ -294,57 +354,127 @@ class decort_disk(DecortController):
], ],
) )
def main(): def check_amodule_args_for_change(self):
decon = decort_disk() check_errors = False
amodule = decon.amodule
# if self.check_aparam_storage_policy_id() is False:
#Full range of Disk status is as follows: check_errors = True
# if self.check_aparam_size() is False:
# "ASSIGNED","MODELED", "CREATING","CREATED","DELETED", "DESTROYED","PURGED", check_errors = True
#
if decon.disk_id: if check_errors:
#disk exist self.exit(fail=True)
if decon.disk_info['status'] in ["MODELED", "CREATING"]:
decon.result['failed'] = True def check_amodule_args_for_create(self):
decon.result['changed'] = False check_errors = False
decon.result['msg'] = ("No change can be done for existing Disk ID {} because of its current "
"status '{}'").format(decon.disk_id, decon.disk_info['status']) aparam_storage_policy_id = self.aparams['storage_policy_id']
# "ASSIGNED","CREATED","DELETED","PURGED", "DESTROYED" if aparam_storage_policy_id is None:
elif decon.disk_info['status'] in ["ASSIGNED","CREATED"]: check_errors = True
if amodule.params['state'] == 'absent': self.message(
decon.delete() msg='Check for parameter "storage_policy_id" failed: '
elif amodule.params['state'] == 'present': 'storage_policy_id must be specified when creating '
decon.action() 'a new disk'
elif decon.disk_info['status'] in ["PURGED", "DESTROYED"]: )
#re-provision disk
if amodule.params['state'] in ('present'): if self.check_aparam_storage_policy_id() is False:
decon.create() check_errors = True
else: if self.check_aparam_size() is False:
decon.nop() check_errors = True
elif decon.disk_info['status'] == "DELETED":
if amodule.params['state'] in ('present'): if check_errors:
decon.action(restore=True) self.exit(fail=True)
elif (amodule.params['state'] == 'absent' and
amodule.params['permanently']): def check_aparam_storage_policy_id(self) -> bool:
decon.delete() check_errors = False
else:
decon.nop() aparam_storage_policy_id = self.aparams['storage_policy_id']
else: if (
# preexisting Disk was not found aparam_storage_policy_id is not None
if amodule.params['state'] == 'absent': and aparam_storage_policy_id
decon.nop() not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
return not check_errors
def check_aparam_size(self) -> bool:
check_errors = False
aparam_size = self.aparams['size']
if (
aparam_size is not None
and aparam_size <= 0
):
check_errors = True
self.message(
msg=(
'Check for parameter "size" failed: '
f'Disk cannot be size {aparam_size}'
),
)
return not check_errors
@DecortController.handle_sdk_exceptions
def run(self):
#
#Full range of Disk status is as follows:
#
# "ASSIGNED","MODELED", "CREATING","CREATED","DELETED", "DESTROYED","PURGED",
#
amodule = self.amodule
if self.disk_id:
#disk exist
if self.disk_info['status'] in ["MODELED", "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'])
# "ASSIGNED","CREATED","DELETED","PURGED", "DESTROYED"
elif self.disk_info['status'] in ["ASSIGNED","CREATED"]:
if amodule.params['state'] == 'absent':
self.delete()
elif amodule.params['state'] == 'present':
self.action()
elif self.disk_info['status'] in ["PURGED", "DESTROYED"]:
#re-provision disk
if amodule.params['state'] in ('present'):
self.create()
else:
self.nop()
elif self.disk_info['status'] == "DELETED":
if amodule.params['state'] in ('present'):
self.action(restore=True)
elif (amodule.params['state'] == 'absent' and
amodule.params['permanently']):
self.delete()
else:
self.nop()
else: else:
decon.create() # preexisting Disk was not found
if amodule.params['state'] == 'absent':
self.nop()
else:
self.create()
if decon.result['failed']: if self.result['failed']:
amodule.fail_json(**decon.result) amodule.fail_json(**self.result)
else: else:
if decon.result['changed'] and amodule.params['state'] in ('present'): if self.result['changed']:
_, decon.disk_info = decon.disk_find(decon.disk_id) _, self.disk_info = self.disk_find(self.disk_id)
decon.result['facts'] = decon.package_facts(amodule.check_mode) self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result) amodule.exit_json(**self.result)
if __name__ == "__main__":
def main():
decort_disk().run()
if __name__ == '__main__':
main() main()
#SHARE

155
library/decort_disk_list.py Normal file
View File

@@ -0,0 +1,155 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_disk_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortDiskList(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(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
sep_id=dict(
type='int',
),
sep_pool_name=dict(
type='str',
),
shared=dict(
type='bool',
),
disk_max_size_gb=dict(
type='int',
),
status=dict(
type='str',
choices=sdk_types.DiskStatus._member_names_,
),
storage_policy_id=dict(
type='int',
),
type=dict(
type='str',
choices=sdk_types.DiskType._member_names_,
),
),
),
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=(
sdk_types.DiskForListAndListDeletedAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
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']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.DiskForListAndListDeletedAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.disks.list(
account_id=aparam_filter['account_id'],
account_name=aparam_filter['account_name'],
disk_max_size_gb=aparam_filter['disk_max_size_gb'],
id=aparam_filter['id'],
name=aparam_filter['name'],
sep_id=aparam_filter['sep_id'],
sep_pool_name=aparam_filter['sep_pool_name'],
shared=aparam_filter['shared'],
status=(
sdk_types.DiskStatus[aparam_status]
if aparam_status else None
),
storage_policy_id=aparam_filter['storage_policy_id'],
type=(
sdk_types.DiskType[aparam_type]
if aparam_type else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortDiskList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,150 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_flip_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortFlipGroupList(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(
account_id=dict(
type='int',
),
client_ids=dict(
type='list',
elements='int',
),
conn_id=dict(
type='int',
),
ext_net_id=dict(
type='int',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
default=False,
),
ip_addr=dict(
type='str',
),
name=dict(
type='str',
),
status=dict(
type='str',
choices=sdk_types.FlipGroupStatus._member_names_,
),
vins_id=dict(
type='int',
),
vins_name=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=(
sdk_types.FlipGroupForListAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.FlipGroupForListAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.flipgroup.list(
account_id=aparam_filter['account_id'],
client_ids=aparam_filter['client_ids'],
conn_id=aparam_filter['conn_id'],
ext_net_id=aparam_filter['ext_net_id'],
id=aparam_filter['id'],
ip_addr=aparam_filter['ip_addr'],
name=aparam_filter['name'],
status=(
sdk_types.FlipGroupStatus[aparam_status]
if aparam_status else None
),
vins_id=aparam_filter['vins_id'],
vins_name=aparam_filter['vins_name'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortFlipGroupList().run()
if __name__ == '__main__':
main()

View File

@@ -36,10 +36,14 @@ class decort_group(DecortController):
group_id=arg_amodule.params['id'], group_id=arg_amodule.params['id'],
group_name=arg_amodule.params['name'], group_name=arg_amodule.params['name'],
) )
self.acc_id = self.bservice_info['accountId']
self.rg_id = self.bservice_info['rgId']
if self.group_id: if self.group_id:
self.group_should_exist = True self.group_should_exist = True
self.check_amodule_args_for_change() self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
return return
def nop(self): def nop(self):
@@ -77,7 +81,7 @@ class decort_group(DecortController):
def create(self): def create(self):
chipset = self.aparams['chipset'] chipset = self.aparams['chipset']
if chipset is None: if chipset is None:
chipset = 'i440fx' chipset = 'Q35'
self.message( self.message(
msg=f'Chipset not specified, ' msg=f'Chipset not specified, '
f'default value "{chipset}" will be used.', f'default value "{chipset}" will be used.',
@@ -92,11 +96,11 @@ class decort_group(DecortController):
arg_ram=self.amodule.params['ram'], arg_ram=self.amodule.params['ram'],
arg_boot_disk=self.amodule.params['boot_disk'], arg_boot_disk=self.amodule.params['boot_disk'],
arg_image_id=self.amodule.params['image_id'], arg_image_id=self.amodule.params['image_id'],
arg_driver=self.amodule.params['driver'],
arg_role=self.amodule.params['role'], arg_role=self.amodule.params['role'],
arg_network=self.amodule.params['networks'], arg_network=self.amodule.params['networks'],
arg_timeout=self.amodule.params['timeoutStart'], arg_timeout=self.amodule.params['timeoutStart'],
chipset=chipset, chipset=chipset,
storage_policy_id=self.aparams['storage_policy_id'],
) )
if self.amodule.params['state'] in ('started','present'): if self.amodule.params['state'] in ('started','present'):
@@ -196,6 +200,7 @@ class decort_group(DecortController):
ret_dict['techStatus'] = self.group_info['techStatus'] ret_dict['techStatus'] = self.group_info['techStatus']
ret_dict['state'] = self.group_info['status'] ret_dict['state'] = self.group_info['status']
ret_dict['Computes'] = self.group_info['computes'] ret_dict['Computes'] = self.group_info['computes']
ret_dict['driver'] = self.group_info['driver']
return ret_dict return ret_dict
@property @property
@@ -229,17 +234,6 @@ class decort_group(DecortController):
image_id=dict( image_id=dict(
type='int', type='int',
), ),
image_name=dict(
type='str',
),
driver=dict(
type='str',
choices=[
'KVM_X86',
'SVA_KVM_X86',
],
default='KVM_X86',
),
boot_disk=dict( boot_disk=dict(
type='int', type='int',
), ),
@@ -287,17 +281,13 @@ class decort_group(DecortController):
'i440fx', 'i440fx',
] ]
), ),
storage_policy_id=dict(
type='int',
),
), ),
supports_check_mode=True, supports_check_mode=True,
required_one_of=[ required_one_of=[
('id', 'name'), ('id', 'name'),
('id', 'networks'),
('id', 'count'),
('id', 'cpu'),
('id', 'ram'),
('id', 'boot_disk'),
('id', 'image_id'),
('id', 'driver'),
], ],
) )
@@ -306,7 +296,7 @@ class decort_group(DecortController):
if ( if (
self.aparams['chipset'] is None self.aparams['chipset'] is None
and self.aparams['count'] > len(self.group_info['computes']) and (self.aparams['count'] if self.aparams['count'] is not None else 0) > len(self.group_info['computes'])
): ):
check_errors = True check_errors = True
self.message( self.message(
@@ -335,58 +325,113 @@ class decort_group(DecortController):
) )
break break
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is not None:
for compute in self.group_info['computes']:
_, compute_info, _ = self._compute_get_by_id(compute['id'])
for disk in compute_info['disks']:
if aparam_storage_policy_id != disk['storage_policy_id']:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" '
'failed: storage_policy_id can not be changed '
f'for compute ID {compute['id']} '
f'disk ID {disk['id']}'
)
if check_errors: if check_errors:
self.exit(fail=True) self.exit(fail=True)
def main(): def check_amodule_args_for_create(self):
subj = decort_group() check_errors = False
amodule = subj.amodule
if amodule.params['state'] == 'check': aparam_storage_policy_id = self.aparams['storage_policy_id']
subj.result['changed'] = False if aparam_storage_policy_id is None:
if subj.group_id: check_errors = True
# cluster is found - package facts and report success to Ansible self.message(
subj.result['failed'] = False msg='Check for parameter "storage_policy_id" failed: '
subj.result['facts'] = subj.package_facts(amodule.check_mode) 'storage_policy_id must be specified when creating '
amodule.exit_json(**subj.result) 'a new group'
# we exit the module at this point )
else: else:
subj.result['failed'] = True if (
subj.result['msg'] = ("Cannot locate Group name '{}'. " aparam_storage_policy_id
"B-service ID {}").format(amodule.params['name'], not in self.rg_info['storage_policy_ids']
amodule.params['bservice_id'],) ):
amodule.fail_json(**subj.result) check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'RG ID {self.rg_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if (
aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if subj.group_id: if check_errors:
if subj.group_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING", self.exit(fail=True)
"ENABLING","DISABLING","RESTORING","MODELED",
"DISABLED","DESTROYED"):
subj.error() @DecortController.handle_sdk_exceptions
elif subj.group_info['status'] in ("DELETED","DESTROYED"): def run(self):
if amodule.params['state'] == 'absent': amodule = self.amodule
subj.nop()
if amodule.params['state'] in ('present','started','stopped'): if amodule.params['state'] == 'check':
subj.create() self.result['changed'] = False
elif subj.group_info['techStatus'] in ("STARTED","STOPPED"): if self.group_id:
if amodule.params['state'] == 'absent': # cluster is found - package facts and report success to Ansible
subj.destroy() self.result['failed'] = False
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
# we exit the module at this point
else: else:
subj.action() self.result['failed'] = True
self.result['msg'] = ("Cannot locate Group name '{}'. "
"B-service ID {}").format(amodule.params['name'],
amodule.params['bservice_id'],)
amodule.fail_json(**self.result)
else: if self.group_id:
if amodule.params['state'] == 'absent': if self.group_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
subj.nop() "ENABLING","DISABLING","RESTORING","MODELED","DESTROYED"):
if amodule.params['state'] in ('present','started','stopped'): self.error()
subj.create() elif self.group_info['status'] in ("DELETED","DESTROYED"):
if amodule.params['state'] == 'absent':
self.nop()
if amodule.params['state'] in ('present','started','stopped'):
self.create()
elif self.group_info['techStatus'] in ("STARTED","STOPPED"):
if amodule.params['state'] == 'absent':
self.destroy()
else:
self.action()
if subj.result['failed']:
amodule.fail_json(**subj.result)
else:
if subj.group_should_exist:
subj.result['facts'] = subj.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result)
else: else:
amodule.exit_json(**subj.result) if amodule.params['state'] == 'absent':
self.nop()
if amodule.params['state'] in ('present','started','stopped'):
self.create()
if __name__ == "__main__": if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.group_should_exist:
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:
amodule.exit_json(**self.result)
def main():
decort_group().run()
if __name__ == '__main__':
main() main()

558
library/decort_image.py Normal file
View File

@@ -0,0 +1,558 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_image
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_image(DecortController):
def __init__(self):
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_name = amodule.params['image_name']
self.validated_virt_image_name = None
self.image_info: dict
self.virt_image_info: dict
if amodule.params['account_name']:
self.validated_account_id, _ = self.account_find(amodule.params['account_name'])
else:
self.validated_account_id = amodule.params['account_id']
if self.validated_account_id == 0:
# we failed either to find or access the specified account - fail the module
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name'])
amodule.fail_json(**self.result)
self.acc_id = self.validated_account_id
if (
self.aparams['virt_id'] != 0
or self.aparams['virt_name'] is not None
):
self.validated_virt_image_id, self.virt_image_info = (
self.decort_virt_image_find(amodule)
)
if self.virt_image_info:
_, linked_image_info = self._image_get_by_id(
image_id=self.virt_image_info['linkTo']
)
self.acc_id = linked_image_info['accountId']
if (
self.aparams['virt_name'] is not None
and self.aparams['virt_name']
!= self.virt_image_info['name']
):
self.decort_virt_image_rename(amodule)
self.result['msg'] = 'Virtual image renamed successfully'
elif (
self.aparams['image_id'] != 0
or self.aparams['image_name'] is not None
):
self.validated_image_id, self.image_info = (
self.decort_image_find(amodule)
)
if self.image_info:
self.acc_id = self.image_info['accountId']
if (
amodule.params['image_name']
and amodule.params['image_name'] != self.image_info['name']
):
decort_image.decort_image_rename(self,amodule)
self.result['msg'] = ("Image renamed successfully")
if self.validated_image_id:
self.check_amodule_args_for_change()
elif self.validated_virt_image_id:
self.check_amodule_args_for_change_virt_image()
elif self.aparams['virt_name']:
self.check_amodule_args_for_create_virt_image()
else:
self.check_amodule_args_for_create_image()
def decort_image_find(self, amodule):
# function that finds the OS image
image_id, image_facts = self.image_find(image_id=amodule.params['image_id'], image_name=self.validated_image_name,
account_id=self.validated_account_id, rg_id=0,
sepid=amodule.params['sep_id'],
pool=amodule.params['pool'])
return image_id, image_facts
def decort_virt_image_find(self, amodule):
# function that finds a virtual image
image_id, image_facts = self.virt_image_find(image_id=amodule.params['virt_id'],
account_id=self.validated_account_id, rg_id=0,
sepid=amodule.params['sep_id'],
virt_name=amodule.params['virt_name'],
pool=amodule.params['pool'])
return image_id, image_facts
def decort_image_create(self,amodule):
aparam_boot = self.aparams['boot']
boot_mode = 'bios'
loader_type = 'unknown'
if aparam_boot is not None:
if aparam_boot['mode'] is None:
self.message(
msg=self.MESSAGES.default_value_used(
param_name='boot.mode',
default_value=boot_mode
),
warning=True,
)
else:
boot_mode = aparam_boot['mode']
if aparam_boot['loader_type'] is None:
self.message(
msg=self.MESSAGES.default_value_used(
param_name='boot.loader_type',
default_value=loader_type
),
warning=True,
)
else:
loader_type = aparam_boot['loader_type']
network_interface_naming = self.aparams['network_interface_naming']
if network_interface_naming is None:
network_interface_naming = 'ens'
self.message(
msg=self.MESSAGES.default_value_used(
param_name='network_interface_naming',
default_value=network_interface_naming
),
warning=True,
)
hot_resize = self.aparams['hot_resize']
if hot_resize is None:
hot_resize = False
self.message(
msg=self.MESSAGES.default_value_used(
param_name='hot_resize',
default_value=hot_resize
),
warning=True,
)
# function that creates OS image
image_facts = self.image_create(
img_name=self.validated_image_name,
url=amodule.params['url'],
boot_mode=boot_mode,
boot_loader_type=loader_type,
hot_resize=hot_resize,
username=amodule.params['image_username'],
password=amodule.params['image_password'],
account_id=self.validated_account_id,
usernameDL=amodule.params['usernameDL'],
passwordDL=amodule.params['passwordDL'],
sepId=amodule.params['sepId'],
poolName=amodule.params['poolName'],
network_interface_naming=network_interface_naming,
storage_policy_id=amodule.params['storage_policy_id'],
)
self.result['changed'] = True
return image_facts
def decort_virt_image_link(self,amodule):
# function that links an OS image to a virtual one
self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.target_image_id)
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.target_image_id,
decort_image.decort_image_package_facts(image_facts)['id'],)
return image_id, image_facts
def decort_image_delete(self,amodule):
# function that removes an image
self.image_delete(imageId=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
def decort_virt_image_create(self,amodule):
# function that creates a virtual image
image_facts = self.virt_image_create(
name=amodule.params['virt_name'],
target_id=self.target_image_id,
account_id=self.aparams['account_id'],
)
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
return image_id, image_facts
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.result['msg'] = ("Image renamed successfully")
image_id, image_facts = decort_image.decort_image_find(self, amodule)
return image_id, image_facts
def decort_virt_image_rename(self, amodule):
image_facts = self.image_rename(imageId=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
@staticmethod
def decort_image_package_facts(
arg_image_facts: dict | None,
arg_check_mode=False,
):
"""Package a dictionary of OS image according to the decort_image module specification. This
dictionary will be returned to the upstream Ansible engine at the completion of the module run.
@param arg_image_facts: dictionary with OS image facts as returned by API call to .../images/list
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode.
@return: dictionary with OS image specs populated from arg_image_facts.
"""
ret_dict = dict(id=0,
name="none",
size=0,
type="none",
state="CHECK_MODE", )
if arg_check_mode:
# in check mode return immediately with the default values
return ret_dict
if arg_image_facts is None:
# if void facts provided - change state value to ABSENT and return
ret_dict['state'] = "ABSENT"
return ret_dict
ret_dict['id'] = arg_image_facts['id']
ret_dict['name'] = arg_image_facts['name']
ret_dict['size'] = arg_image_facts['size']
# ret_dict['arch'] = arg_image_facts['architecture']
ret_dict['sep_id'] = arg_image_facts['sepId']
ret_dict['pool'] = arg_image_facts['pool']
ret_dict['state'] = arg_image_facts['status']
ret_dict['linkto'] = arg_image_facts['linkTo']
ret_dict['accountId'] = arg_image_facts['accountId']
ret_dict['boot_mode'] = arg_image_facts['bootType']
ret_dict['boot_loader_type'] = ''
match arg_image_facts['type']:
case 'cdrom' | 'virtual' as type:
ret_dict['type'] = type
case _ as boot_loader_type:
ret_dict['type'] = 'template'
ret_dict['boot_loader_type'] = boot_loader_type
ret_dict['network_interface_naming'] = arg_image_facts[
'networkInterfaceNaming'
]
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']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
pool=dict(
type='str',
default='',
),
sep_id=dict(
type='int',
default=0,
),
account_name=dict(
type='str',
),
account_id=dict(
type='int',
),
image_name=dict(
type='str',
),
image_id=dict(
type='int',
default=0,
),
virt_id=dict(
type='int',
default=0,
),
virt_name=dict(
type='str',
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'present',
],
),
url=dict(
type='str',
),
sepId=dict(
type='int',
default=0,
),
poolName=dict(
type='str',
),
hot_resize=dict(
type='bool',
),
image_username=dict(
type='str',
),
image_password=dict(
type='str',
),
usernameDL=dict(
type='str',
),
passwordDL=dict(
type='str',
),
boot=dict(
type='dict',
options=dict(
mode=dict(
type='str',
choices=[
'bios',
'uefi',
],
),
loader_type=dict(
type='str',
choices=[
'windows',
'linux',
'unknown',
],
),
),
),
network_interface_naming=dict(
type='str',
choices=[
'ens',
'eth',
],
),
storage_policy_id=dict(
type='int',
),
),
supports_check_mode=True,
)
def check_amodule_args_for_change(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if (
aparam_storage_policy_id is not None
and aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_change_virt_image(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if (
aparam_storage_policy_id is not None
and (
aparam_storage_policy_id
!= self.virt_image_info['storage_policy_id']
)
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id can not be changed in virtual image'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create_image(self):
check_errors = False
aparam_account_id = self.aparams['account_id']
if aparam_account_id is None:
check_errors = True
self.message(
msg='Check for parameter "account_id" failed: '
'account_id must be specified when creating '
'a new image'
)
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id must be specified when creating '
'a new image'
)
elif (
aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create_virt_image(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is not None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id can not be specified when creating '
'virtual image'
)
if check_errors:
self.exit(fail=True)
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
if amodule.params['virt_name'] or amodule.params['virt_id']:
image_id, image_facts = self.decort_virt_image_find(amodule)
if amodule.params['image_name'] or amodule.params['image_id']:
self.target_image_id, _ = self.decort_image_find(amodule)
else:
self.target_image_id = 0
if self.decort_image_package_facts(image_facts)['id'] > 0:
self.result['facts'] = self.decort_image_package_facts(image_facts, amodule.check_mode)
self.validated_virt_image_id = self.decort_image_package_facts(image_facts)['id']
self.validated_virt_image_name = self.decort_image_package_facts(image_facts)['name']
if self.decort_image_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and self.target_image_id > 0:
image_id, image_facts = self.decort_virt_image_create(amodule)
self.result['msg'] = ("Virtual image '{}' created").format(self.decort_image_package_facts(image_facts)['id'])
self.result['changed'] = True
elif self.decort_image_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and self.target_image_id == 0:
self.result['msg'] = ("Cannot find OS image")
amodule.fail_json(**self.result)
if self.validated_virt_image_id:
if (
self.target_image_id
and self.decort_image_package_facts(image_facts)[
'linkto'
] != self.target_image_id
):
self.decort_virt_image_link(amodule)
self.result['changed'] = True
amodule.exit_json(**self.result)
if (
amodule.params['storage_policy_id'] is not None
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
self.image_change_storage_policy(
image_id=self.validated_virt_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
if amodule.params['state'] == "absent" and self.validated_virt_image_id:
amodule.image_id_delete = self.validated_virt_image_id
image_id, image_facts = self.decort_virt_image_find(amodule)
if image_facts['status'] != 'PURGED':
self.decort_image_delete(amodule)
elif amodule.params['image_name'] or amodule.params['image_id']:
image_id, image_facts = self.decort_image_find(amodule)
self.validated_image_id = self.decort_image_package_facts(image_facts)['id']
if self.decort_image_package_facts(image_facts)['id'] > 0:
self.result['facts'] = self.decort_image_package_facts(image_facts, amodule.check_mode)
if amodule.params['state'] == "present" and self.validated_image_id == 0 and amodule.params['image_name'] and amodule.params['url']:
self.decort_image_create(amodule)
self.result['changed'] = True
image_id, image_facts = self.decort_image_find(amodule)
self.result['msg'] = ("OS image '{}' created").format(self.decort_image_package_facts(image_facts)['id'])
self.result['facts'] = self.decort_image_package_facts(image_facts, amodule.check_mode)
self.validated_image_id = self.decort_image_package_facts(image_facts)['id']
elif amodule.params['state'] == "absent" and self.validated_image_id:
amodule.image_id_delete = self.validated_image_id
image_id, image_facts = self.decort_image_find(amodule)
if image_facts['status'] != 'DESTROYED':
self.decort_image_delete(amodule)
if self.validated_image_id:
if (
amodule.params['storage_policy_id'] is not None
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
self.image_change_storage_policy(
image_id=self.validated_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
if self.result['failed'] == True:
# we failed to find the specified image - fail the module
self.result['changed'] = False
amodule.fail_json(**self.result)
else:
if self.validated_image_id:
_, image_facts = self.decort_image_find(amodule=amodule)
elif self.validated_virt_image_id:
_, image_facts = self.decort_virt_image_find(amodule=amodule)
self.result['facts'] = self.decort_image_package_facts(
arg_image_facts=image_facts,
arg_check_mode=amodule.check_mode,
)
amodule.exit_json(**self.result)
def main():
decort_image().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,162 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_image_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortImageList(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(
bootable=dict(
type='bool',
),
id=dict(
type='int',
),
enabled=dict(
type='bool',
),
hot_resize=dict(
type='bool',
),
size_gb=dict(
type='int',
),
name=dict(
type='str',
),
public=dict(
type='bool',
),
sep_id=dict(
type='int',
),
sep_name=dict(
type='str',
),
sep_pool_name=dict(
type='str',
),
status=dict(
type='str',
choices=sdk_types.ImageStatus._member_names_,
),
type=dict(
type='str',
choices=sdk_types.ImageType._member_names_,
),
storage_policy_id=dict(
type='int',
),
),
),
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=(
sdk_types.ImageForListAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
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']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.ImageForListAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.ca.image.list(
bootable=aparam_filter['bootable'],
enabled=aparam_filter['enabled'],
hot_resize=aparam_filter['hot_resize'],
id=aparam_filter['id'],
name=aparam_filter['name'],
public=aparam_filter['public'],
sep_id=aparam_filter['sep_id'],
sep_name=aparam_filter['sep_name'],
sep_pool_name=aparam_filter['sep_pool_name'],
size_gb=aparam_filter['size_gb'],
status=(
sdk_types.ImageStatus[aparam_status]
if aparam_status else None
),
storage_policy_id=aparam_filter['storage_policy_id'],
type=(
sdk_types.ImageType[aparam_type]
if aparam_type else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortImageList().run()
if __name__ == '__main__':
main()

View File

@@ -25,6 +25,7 @@ class DecortJWT(DecortController):
return amodule_init_args return amodule_init_args
@DecortController.handle_sdk_exceptions
def run(self): def run(self):
self.result['jwt'] = self.jwt self.result['jwt'] = self.jwt
self.amodule.exit_json(**self.result) self.amodule.exit_json(**self.result)

View File

@@ -33,10 +33,10 @@ class decort_k8s(DecortController):
'taints': [], 'taints': [],
'annotations': [], 'annotations': [],
'ci_user_data': {}, 'ci_user_data': {},
'chipset': 'i440fx', 'chipset': 'Q35',
} }
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] is None: if arg_amodule.params['name'] is None and arg_amodule.params['id'] is None:
self.result['failed'] = True self.result['failed'] = True
self.result['changed'] = False self.result['changed'] = False
self.result['msg'] = "Cannot manage k8s cluster when its ID is 0 and name is empty." self.result['msg'] = "Cannot manage k8s cluster when its ID is 0 and name is empty."
@@ -158,7 +158,7 @@ class decort_k8s(DecortController):
def create(self): def create(self):
master_chipset = self.amodule.params['master_chipset'] master_chipset = self.amodule.params['master_chipset']
if master_chipset is None: if master_chipset is None:
master_chipset = 'i440fx' master_chipset = 'Q35'
target_wgs = deepcopy(self.amodule.params['workers']) target_wgs = deepcopy(self.amodule.params['workers'])
for wg in target_wgs: for wg in target_wgs:
@@ -166,34 +166,36 @@ class decort_k8s(DecortController):
if wg[param] is None: if wg[param] is None:
wg[param] = default_value wg[param] = default_value
k8s_id = self.k8s_provision(self.amodule.params['name'], k8s_id = self.k8s_provision(
self.amodule.params['k8ci_id'], self.amodule.params['name'],
self.amodule.params['rg_id'], self.amodule.params['k8ci_id'],
self.amodule.params['vins_id'], self.amodule.params['rg_id'],
self.amodule.params['network_plugin'], self.amodule.params['vins_id'],
self.amodule.params['master_count'], self.amodule.params['network_plugin'],
self.amodule.params['master_cpu'], self.amodule.params['master_count'],
self.amodule.params['master_ram'], self.amodule.params['master_cpu'],
self.amodule.params['master_disk'], self.amodule.params['master_ram'],
self.amodule.params['master_sepid'], self.amodule.params['master_disk'],
self.amodule.params['master_pool'], self.amodule.params['master_sepid'],
target_wgs[0], self.amodule.params['master_pool'],
self.amodule.params['extnet_id'], target_wgs[0],
self.amodule.params['with_lb'], self.amodule.params['extnet_id'],
self.amodule.params['ha_lb'], self.amodule.params['with_lb'],
self.amodule.params['additionalSANs'], self.amodule.params['ha_lb'],
self.amodule.params['init_conf'], self.amodule.params['additionalSANs'],
self.amodule.params['cluster_conf'], self.amodule.params['init_conf'],
self.amodule.params['kublet_conf'], self.amodule.params['cluster_conf'],
self.amodule.params['kubeproxy_conf'], self.amodule.params['kublet_conf'],
self.amodule.params['join_conf'], self.amodule.params['kubeproxy_conf'],
self.amodule.params['oidc_cert'], self.amodule.params['join_conf'],
self.amodule.params['description'], self.amodule.params['oidc_cert'],
self.amodule.params['extnet_only'], self.amodule.params['description'],
master_chipset, self.amodule.params['extnet_only'],
lb_sysctl=self.amodule.params['lb_sysctl'], master_chipset=master_chipset,
zone_id=self.aparams['zone_id'], lb_sysctl=self.amodule.params['lb_sysctl'],
) zone_id=self.aparams['zone_id'],
storage_policy_id=self.aparams['storage_policy_id'],
)
if not k8s_id: if not k8s_id:
if k8s_id == 0: if k8s_id == 0:
@@ -241,6 +243,12 @@ class decort_k8s(DecortController):
) )
self.exit(fail=True) self.exit(fail=True)
if (
self.aparams['name'] is not None
and self.aparams['name'] != self.k8s_info['name']
):
self.k8s_update(id=self.k8s_id, name=self.aparams['name'])
if preupdate: if preupdate:
# K8s info updating # 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)
@@ -275,9 +283,6 @@ class decort_k8s(DecortController):
type='str', type='str',
default='', default='',
), ),
quotas=dict(
type='dict',
),
state=dict( state=dict(
type='str', type='str',
default='present', default='present',
@@ -296,7 +301,6 @@ class decort_k8s(DecortController):
), ),
name=dict( name=dict(
type='str', type='str',
default='',
), ),
id=dict( id=dict(
type='int', type='int',
@@ -448,6 +452,9 @@ class decort_k8s(DecortController):
zone_id=dict( zone_id=dict(
type='int', type='int',
), ),
storage_policy_id=dict(
type='int',
),
), ),
supports_check_mode=True, supports_check_mode=True,
required_one_of=[ required_one_of=[
@@ -499,6 +506,32 @@ class decort_k8s(DecortController):
'K8s cluster must be stopped to migrate to a zone.' 'K8s cluster must be stopped to migrate to a zone.'
) )
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'
]:
computes_ids.append(master_node['id'])
for wg in self.k8s_info['k8sGroups']['workers']:
workers_ids = [
worker['id'] for worker in wg['detailedInfo']
]
computes_ids.extend(workers_ids)
for compute_id in computes_ids:
_, compute_info, _ = self._compute_get_by_id(
comp_id=compute_id
)
for disk in compute_info['disks']:
if aparam_storage_policy_id != disk['storage_policy_id']:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" '
'failed: storage_policy_id can not be changed '
f'for k8s cluster ID {self.k8s_id} compute ID '
f'{compute_id} disk ID {disk['id']}'
)
if check_errors: if check_errors:
self.exit(fail=True) self.exit(fail=True)
@@ -540,71 +573,96 @@ class decort_k8s(DecortController):
if self.check_aparam_zone_id() is False: if self.check_aparam_zone_id() is False:
check_errors = True check_errors = True
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id must be specified when creating '
'a new cluster'
)
elif (
aparam_storage_policy_id
not in self.rg_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'RG ID {self.rg_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors: if check_errors:
self.exit(fail=True) self.exit(fail=True)
def main(): @DecortController.handle_sdk_exceptions
subj = decort_k8s() def run(self):
amodule = subj.amodule amodule = self.amodule
if subj.amodule.check_mode: if self.amodule.check_mode:
subj.result['changed'] = False self.result['changed'] = False
if subj.k8s_id: if self.k8s_id:
# cluster is found - package facts and report success to Ansible # cluster is found - package facts and report success to Ansible
subj.result['failed'] = False self.result['failed'] = False
subj.result['facts'] = subj.package_facts(amodule.check_mode) self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**subj.result) amodule.exit_json(**self.result)
# we exit the module at this point # we exit the module at this point
else:
subj.result['failed'] = True
subj.result['msg'] = ("Cannot locate K8s cluster name '{}'. "
"RG ID {}").format(amodule.params['name'],
amodule.params['rg_id'],)
amodule.fail_json(**subj.result)
if subj.k8s_id:
if subj.k8s_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED"):
subj.error()
elif subj.k8s_info['status'] == "DELETED":
if amodule.params['state'] in (
'disabled', 'enabled', 'present', 'started', 'stopped'
):
subj.k8s_restore(subj.k8s_id)
subj.action(disared_state=amodule.params['state'],
preupdate=True)
if amodule.params['state'] == 'absent':
if amodule.params['permanent']:
subj.destroy()
else:
subj.nop()
elif subj.k8s_info['status'] in ('ENABLED', 'DISABLED'):
if amodule.params['state'] == 'absent':
subj.destroy()
else: else:
subj.action(disared_state=amodule.params['state']) self.result['failed'] = True
elif subj.k8s_info['status'] == "DESTROYED": self.result['msg'] = ("Cannot locate K8s cluster name '{}'. "
if amodule.params['state'] in ('present','enabled'): "RG ID {}").format(amodule.params['name'],
subj.create() amodule.params['rg_id'],)
if amodule.params['state'] == 'absent': amodule.fail_json(**self.result)
subj.nop()
else:
if amodule.params['state'] == 'absent':
subj.nop()
if amodule.params['state'] in ('present','started'):
subj.create()
elif amodule.params['state'] in ('stopped', 'disabled','enabled'):
subj.error()
if subj.result['failed']: if self.k8s_id:
amodule.fail_json(**subj.result) if self.k8s_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
else: "ENABLING","DISABLING","RESTORING","MODELED"):
if subj.k8s_should_exist: self.error()
subj.result['facts'] = subj.package_facts(amodule.check_mode) elif self.k8s_info['status'] == "DELETED":
amodule.exit_json(**subj.result) if amodule.params['state'] in (
'disabled', 'enabled', 'present', 'started', 'stopped'
):
self.k8s_restore(self.k8s_id)
self.action(disared_state=amodule.params['state'],
preupdate=True)
if amodule.params['state'] == 'absent':
if amodule.params['permanent']:
self.destroy()
else:
self.nop()
elif self.k8s_info['status'] in ('ENABLED', 'DISABLED'):
if amodule.params['state'] == 'absent':
self.destroy()
else:
self.action(disared_state=amodule.params['state'])
elif self.k8s_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'):
self.create()
if amodule.params['state'] == 'absent':
self.nop()
else: else:
amodule.exit_json(**subj.result) if amodule.params['state'] == 'absent':
self.nop()
if amodule.params['state'] in ('present','started'):
self.create()
elif amodule.params['state'] in ('stopped', 'disabled','enabled'):
self.error()
if __name__ == "__main__": if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.k8s_should_exist:
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:
amodule.exit_json(**self.result)
def main():
decort_k8s().run()
if __name__ == '__main__':
main() main()

File diff suppressed because it is too large Load Diff

View File

@@ -42,8 +42,8 @@ class decort_lb(DecortController):
if not self.lb_id: if not self.lb_id:
self.result['failed'] = True self.result['failed'] = True
self.result['msg'] = "Specified LB ID {} not found."\ self.result['msg'] = "Specified LB ID {} not found."\
.format(arg_amodule.params['lb _id']) .format(arg_amodule.params['lb_id'])
self.fail_json(**self.result) self.amodule.fail_json(**self.result)
self.rg_id = self.lb_facts['rgId'] self.rg_id = self.lb_facts['rgId']
self.vins_id = self.lb_facts['vinsId'] self.vins_id = self.lb_facts['vinsId']
@@ -51,12 +51,11 @@ class decort_lb(DecortController):
self.rg_id, self.rg_facts = self.rg_find(0,arg_amodule.params['rg_id'], arg_rg_name="") self.rg_id, self.rg_facts = self.rg_find(0,arg_amodule.params['rg_id'], arg_rg_name="")
if not self.rg_id: if not self.rg_id:
self.result['failed'] = True self.result['failed'] = True
self.result['msg'] = "Specified RG ID {} not found.".format(arg_amodule.params['vins_id']) self.result['msg'] = "Specified RG ID {} not found.".format(arg_amodule.params['rg_id'])
self.amodule.fail_json(**self.result) self.amodule.fail_json(**self.result)
self.acc_id = self.rg_facts['accountId'] self.acc_id = self.rg_facts['accountId']
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "": elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
if not arg_amodule.params['rg_name']: if not arg_amodule.params['rg_name']:
self.result['failed'] = True self.result['failed'] = True
self.result['msg'] = ("RG name must be specified with account present") self.result['msg'] = ("RG name must be specified with account present")
@@ -163,8 +162,9 @@ class decort_lb(DecortController):
def delete(self): def delete(self):
self.lb_delete(self.lb_id, self.amodule.params['permanently']) self.lb_delete(self.lb_id, self.amodule.params['permanently'])
self.lb_facts['status'] = 'DESTROYED' self.lb_id, self.lb_facts = self._lb_get_by_id(self.lb_id)
return return
def nop(self): def nop(self):
"""No operation (NOP) handler for LB management by decort_lb module. """No operation (NOP) handler for LB management by decort_lb module.
This function is intended to be called from the main switch construct of the module This function is intended to be called from the main switch construct of the module
@@ -252,10 +252,6 @@ class decort_lb(DecortController):
type='int', type='int',
default=0, default=0,
), ),
ext_ip_addr=dict(
type='str',
default='',
),
state=dict( state=dict(
type='str', type='str',
choices=[ choices=[
@@ -365,56 +361,60 @@ class decort_lb(DecortController):
if check_errors: if check_errors:
self.exit(fail=True) self.exit(fail=True)
@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"]:
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'):
if amodule.params['state'] == 'absent':
self.delete()
else:
self.action(d_state=amodule.params['state'])
elif self.lb_facts['status'] == "DELETED":
if amodule.params['state'] == 'present':
self.action(restore=True)
elif amodule.params['state'] == 'enabled':
self.action(d_state='enabled', restore=True)
elif (amodule.params['state'] == 'absent' and
amodule.params['permanently']):
self.delete()
elif amodule.params['state'] == 'disabled':
self.error()
elif self.lb_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
self.create()
elif amodule.params['state'] == 'absent':
self.nop()
elif amodule.params['state'] == 'disabled':
self.error()
else:
state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
self.nop()
elif state in ('present', 'enabled', 'stopped', 'started'):
self.create()
elif state == 'disabled':
self.error()
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.result['changed']:
_, self.lb_facts = self.lb_find(lb_id=self.lb_id)
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
def main(): def main():
decon = decort_lb() decort_lb().run()
amodule = decon.amodule
if decon.lb_id:
if decon.lb_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING","DESTROYING","RESTORING"]:
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("No change can be done for existing LB ID {} because of its current "
"status '{}'").format(decon.lb_id, decon.lb_facts['status'])
elif decon.lb_facts['status'] in ('DISABLED', 'ENABLED', 'CREATED'):
if amodule.params['state'] == 'absent':
decon.delete()
else:
decon.action(d_state=amodule.params['state'])
elif decon.lb_facts['status'] == "DELETED":
if amodule.params['state'] == 'present':
decon.action(restore=True)
elif amodule.params['state'] == 'enabled':
decon.action(d_state='enabled', restore=True)
elif (amodule.params['state'] == 'absent' and
amodule.params['permanently']):
decon.delete()
elif amodule.params['state'] == 'disabled':
decon.error()
elif decon.lb_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
decon.create()
elif amodule.params['state'] == 'absent':
decon.nop()
elif amodule.params['state'] == 'disabled':
decon.error()
else:
state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
decon.nop()
elif state in ('present', 'enabled', 'stopped', 'started'):
decon.create()
elif state == 'disabled':
decon.error()
if decon.result['failed']:
amodule.fail_json(**decon.result) if __name__ == '__main__':
else:
if decon.result['changed'] and amodule.params['state'] != 'absent':
_, decon.lb_facts = decon.lb_find(decon.lb_id)
if decon.lb_id:
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main() main()

View File

@@ -4,397 +4,59 @@ DOCUMENTATION = r'''
--- ---
module: decort_osimage module: decort_osimage
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_osimage(DecortController):
def __init__(self):
super(decort_osimage, self).__init__(AnsibleModule(**self.amodule_init_args))
amodule = self.amodule
self.validated_image_id = 0
self.validated_virt_image_id = 0
self.validated_image_name = amodule.params['image_name']
self.validated_virt_image_name = None
self.validated_virt_image_id = amodule.params['virt_id']
if amodule.params['account_name']:
self.validated_account_id, _ = self.account_find(amodule.params['account_name'])
else:
self.validated_account_id = amodule.params['account_id']
if self.validated_account_id == 0:
# we failed either to find or access the specified account - fail the module
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name'])
amodule.fail_json(**self.result)
if amodule.params['virt_id'] != 0 and amodule.params['virt_name']:
self.validated_virt_image_id, image_facts =\
self.decort_virt_image_find(amodule)
if (self.validated_virt_image_id and
amodule.params['virt_name'] != image_facts['name']):
self.decort_virt_image_rename(amodule)
self.result['msg'] = 'Virtual image renamed successfully'
elif amodule.params['image_id'] != 0 and amodule.params['image_name']:
self.validated_image_id, image_facts = self.decort_image_find(amodule)
if (self.validated_image_id and
amodule.params['image_name'] != image_facts['name']):
decort_osimage.decort_image_rename(self,amodule)
self.result['msg'] = ("Image renamed successfully")
def decort_image_find(self, amodule):
# function that finds the OS image
image_id, image_facts = self.image_find(image_id=amodule.params['image_id'], image_name=self.validated_image_name,
account_id=self.validated_account_id, rg_id=0,
sepid=amodule.params['sep_id'],
pool=amodule.params['pool'])
return image_id, image_facts
def decort_virt_image_find(self, amodule):
# function that finds a virtual image
image_id, image_facts = self.virt_image_find(image_id=amodule.params['virt_id'],
account_id=self.validated_account_id, rg_id=0,
sepid=amodule.params['sep_id'],
virt_name=amodule.params['virt_name'],
pool=amodule.params['pool'])
return image_id, image_facts
def decort_image_create(self,amodule):
aparam_boot = self.aparams['boot']
boot_mode = 'bios'
loader_type = 'unknown'
if aparam_boot is not None:
if aparam_boot['mode'] is None:
self.message(
msg=self.MESSAGES.default_value_used(
param_name='boot.mode',
default_value=boot_mode
),
warning=True,
)
else:
boot_mode = aparam_boot['mode']
if aparam_boot['loader_type'] is None:
self.message(
msg=self.MESSAGES.default_value_used(
param_name='boot.loader_type',
default_value=loader_type
),
warning=True,
)
else:
loader_type = aparam_boot['loader_type']
network_interface_naming = self.aparams['network_interface_naming']
if network_interface_naming is None:
network_interface_naming = 'ens'
self.message(
msg=self.MESSAGES.default_value_used(
param_name='network_interface_naming',
default_value=network_interface_naming
),
warning=True,
)
hot_resize = self.aparams['hot_resize']
if hot_resize is None:
hot_resize = False
self.message(
msg=self.MESSAGES.default_value_used(
param_name='hot_resize',
default_value=hot_resize
),
warning=True,
)
# function that creates OS image
image_facts = self.image_create(img_name=self.validated_image_name,
url=amodule.params['url'],
gid=amodule.params['gid'],
boot_mode=boot_mode,
boot_loader_type=loader_type,
hot_resize=hot_resize,
username=amodule.params['image_username'],
password=amodule.params['image_password'],
account_id=self.validated_account_id,
usernameDL=amodule.params['usernameDL'],
passwordDL=amodule.params['passwordDL'],
sepId=amodule.params['sepId'],
poolName=amodule.params['poolName'],
drivers=amodule.params['drivers'],
network_interface_naming=network_interface_naming)
self.result['changed'] = True
return image_facts
def decort_virt_image_link(self,amodule):
# function that links an OS image to a virtual one
self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.target_image_id)
image_id, image_facts = decort_osimage.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.target_image_id,
decort_osimage.decort_osimage_package_facts(image_facts)['id'],)
return image_id, image_facts
def decort_image_delete(self,amodule):
# function that removes an image
self.image_delete(imageId=amodule.image_id_delete)
self.result['changed'] = True
self.result['msg'] = ("Image '{}' deleted").format(amodule.image_id_delete)
def decort_virt_image_create(self,amodule):
# function that creates a virtual image
image_facts = self.virt_image_create(
name=amodule.params['virt_name'],
target_id=self.target_image_id,
account_id=self.aparams['account_id'],
)
image_id, image_facts = decort_osimage.decort_virt_image_find(self, amodule)
self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
return image_id, image_facts
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.result['msg'] = ("Image renamed successfully")
image_id, image_facts = decort_osimage.decort_image_find(self, amodule)
return image_id, image_facts
def decort_virt_image_rename(self, amodule):
image_facts = self.image_rename(imageId=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
@staticmethod
def decort_osimage_package_facts(
arg_osimage_facts: dict | None,
arg_check_mode=False,
):
"""Package a dictionary of OS image according to the decort_osimage module specification. This
dictionary will be returned to the upstream Ansible engine at the completion of the module run.
@param arg_osimage_facts: dictionary with OS image facts as returned by API call to .../images/list
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode.
@return: dictionary with OS image specs populated from arg_osimage_facts.
"""
ret_dict = dict(id=0,
name="none",
size=0,
type="none",
state="CHECK_MODE", )
if arg_check_mode:
# in check mode return immediately with the default values
return ret_dict
if arg_osimage_facts is None:
# if void facts provided - change state value to ABSENT and return
ret_dict['state'] = "ABSENT"
return ret_dict
ret_dict['id'] = arg_osimage_facts['id']
ret_dict['name'] = arg_osimage_facts['name']
ret_dict['size'] = arg_osimage_facts['size']
# ret_dict['arch'] = arg_osimage_facts['architecture']
ret_dict['sep_id'] = arg_osimage_facts['sepId']
ret_dict['pool'] = arg_osimage_facts['pool']
ret_dict['state'] = arg_osimage_facts['status']
ret_dict['linkto'] = arg_osimage_facts['linkTo']
ret_dict['accountId'] = arg_osimage_facts['accountId']
ret_dict['boot_mode'] = arg_osimage_facts['bootType']
ret_dict['boot_loader_type'] = ''
match arg_osimage_facts['type']:
case 'cdrom' | 'virtual' as type:
ret_dict['type'] = type
case _ as boot_loader_type:
ret_dict['type'] = 'template'
ret_dict['boot_loader_type'] = boot_loader_type
ret_dict['network_interface_naming'] = arg_osimage_facts[
'networkInterfaceNaming'
]
ret_dict['hot_resize'] = arg_osimage_facts['hotResize']
return ret_dict
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
pool=dict(
type='str',
default='',
),
sep_id=dict(
type='int',
default=0,
),
account_name=dict(
type='str',
),
account_id=dict(
type='int',
),
image_name=dict(
type='str',
),
image_id=dict(
type='int',
default=0,
),
virt_id=dict(
type='int',
default=0,
),
virt_name=dict(
type='str',
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'present',
],
),
drivers=dict(
type='str',
default='KVM_X86',
),
url=dict(
type='str',
),
gid=dict(
type='int',
default=0,
),
sepId=dict(
type='int',
default=0,
),
poolName=dict(
type='str',
),
hot_resize=dict(
type='bool',
),
image_username=dict(
type='str',
),
image_password=dict(
type='str',
),
usernameDL=dict(
type='str',
),
passwordDL=dict(
type='str',
),
boot=dict(
type='dict',
options=dict(
mode=dict(
type='str',
choices=[
'bios',
'uefi',
],
),
loader_type=dict(
type='str',
choices=[
'windows',
'linux',
'unknown',
],
),
),
),
network_interface_naming=dict(
type='str',
choices=[
'ens',
'eth',
],
),
),
supports_check_mode=True,
)
def main(): def main():
decon = decort_osimage() module = AnsibleModule(
amodule = decon.amodule argument_spec=dict(
if amodule.params['virt_name'] or amodule.params['virt_id']: app_id=dict(type='raw'),
app_secret=dict(type='raw'),
authenticator=dict(type='raw'),
controller_url=dict(type='raw'),
domain=dict(type='raw'),
jwt=dict(type='raw'),
oauth2_url=dict(type='raw'),
password=dict(type='raw'),
username=dict(type='raw'),
verify_ssl=dict(type='raw'),
ignore_api_compatibility=dict(type='raw'),
ignore_sdk_version_check=dict(type='raw'),
pool=dict(type='raw'),
sep_id=dict(type='raw'),
account_name=dict(type='raw'),
account_id=dict(type='raw'),
image_name=dict(type='raw'),
image_id=dict(type='raw'),
virt_id=dict(type='raw'),
virt_name=dict(type='raw'),
state=dict(type='raw'),
url=dict(type='raw'),
sepId=dict(type='raw'),
poolName=dict(type='raw'),
hot_resize=dict(type='raw'),
image_username=dict(type='raw'),
image_password=dict(type='raw'),
usernameDL=dict(type='raw'),
passwordDL=dict(type='raw'),
boot=dict(type='raw'),
network_interface_naming=dict(type='raw'),
storage_policy_id=dict(type='raw'),
),
supports_check_mode=True,
)
image_id, image_facts = decort_osimage.decort_virt_image_find(decon, amodule) module.fail_json(
if amodule.params['image_name'] or amodule.params['image_id']: msg=(
decon.target_image_id, _ = decort_osimage.decort_image_find(decon, amodule) 'The module "decort_osimage" has been renamed to "decort_image". '
else: 'Please update your playbook to use "decort_image" '
decon.target_image_id = 0 'instead of "decort_osimage".'
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] > 0: ),
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode) )
decon.validated_virt_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
decon.validated_virt_image_name = decort_osimage.decort_osimage_package_facts(image_facts)['name']
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.target_image_id > 0:
image_id, image_facts = decort_osimage.decort_virt_image_create(decon,amodule)
decon.result['msg'] = ("Virtual image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id'])
decon.result['changed'] = True
elif decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.target_image_id == 0:
decon.result['msg'] = ("Cannot find OS image")
amodule.fail_json(**decon.result)
if decon.validated_virt_image_id and decon.target_image_id:
if decort_osimage.decort_osimage_package_facts(image_facts)['linkto'] != decon.target_image_id:
decort_osimage.decort_virt_image_link(decon,amodule)
decon.result['changed'] = True
amodule.exit_json(**decon.result)
if decon.validated_virt_image_id > 0 and amodule.params['state'] == "absent":
amodule.image_id_delete = decon.validated_virt_image_id
decort_osimage.decort_image_delete(decon, amodule)
elif amodule.params['image_name'] or amodule.params['image_id']:
image_id, image_facts = decort_osimage.decort_image_find(decon, amodule)
decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] > 0:
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
if amodule.params['state'] == "present" and decon.validated_image_id == 0 and amodule.params['image_name'] and amodule.params['url']:
decort_osimage.decort_image_create(decon,amodule)
decon.result['changed'] = True
image_id, image_facts = decort_osimage.decort_image_find(decon, amodule)
decon.result['msg'] = ("OS image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id'])
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
elif amodule.params['state'] == "absent" and decon.validated_image_id:
amodule.image_id_delete = decon.validated_image_id
decort_osimage.decort_image_delete(decon,amodule)
if decon.result['failed'] == True:
# we failed to find the specified image - fail the module
decon.result['changed'] = False
amodule.fail_json(**decon.result)
amodule.exit_json(**decon.result)
if __name__ == "__main__": if __name__ == '__main__':
main() main()

View File

@@ -86,60 +86,65 @@ class decort_pfw(DecortController):
return return
@DecortController.handle_sdk_exceptions
def run(self):
amodule = self.amodule
pfw_facts = None # will hold PFW facts as returned by pfw_configure
#
# Validate module arguments:
# 1) specified Compute instance exists in correct state
# 2) specified ViNS exists
# 3) ViNS has GW function
# 4) Compute is connected to this ViNS
#
validated_comp_id, comp_facts, rg_id = self.compute_find(amodule.params['compute_id'])
if not validated_comp_id:
self.result['failed'] = True
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)
gw_vnf_facts = vins_facts['vnfs'].get('GW')
if not gw_vnf_facts or gw_vnf_facts['status'] == "DESTROYED":
self.result['failed'] = True
self.result['msg'] = "ViNS ID {} does not have a configured external connection.".format(validated_vins_id)
amodule.fail_json(**self.result)
#
# Initial validation of module arguments is complete
#
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)
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'])
else:
pfw_facts = self._pfw_get(comp_facts['id'], vins_facts['id'])
#
# complete module run
#
if self.result['failed']:
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)
amodule.exit_json(**self.result)
def main(): def main():
decon = decort_pfw() decort_pfw().run()
amodule = decon.amodule
pfw_facts = None # will hold PFW facts as returned by pfw_configure
# if __name__ == '__main__':
# Validate module arguments:
# 1) specified Compute instance exists in correct state
# 2) specified ViNS exists
# 3) ViNS has GW function
# 4) Compute is connected to this ViNS
#
validated_comp_id, comp_facts, rg_id = decon.compute_find(amodule.params['compute_id'])
if not validated_comp_id:
decon.result['failed'] = True
decon.result['msg'] = "Cannot find specified Compute ID {}.".format(amodule.params['compute_id'])
amodule.fail_json(**decon.result)
validated_vins_id, vins_facts = decon.vins_find(amodule.params['vins_id'])
if not validated_vins_id:
decon.result['failed'] = True
decon.result['msg'] = "Cannot find specified ViNS ID {}.".format(amodule.params['vins_id'])
amodule.fail_json(**decon.result)
gw_vnf_facts = vins_facts['vnfs'].get('GW')
if not gw_vnf_facts or gw_vnf_facts['status'] == "DESTROYED":
decon.result['failed'] = True
decon.result['msg'] = "ViNS ID {} does not have a configured external connection.".format(validated_vins_id)
amodule.fail_json(**decon.result)
#
# Initial validation of module arguments is complete
#
if amodule.params['state'] == 'absent':
# ignore amodule.params['rules'] and remove all rules associated with this Compute
pfw_facts = decon.pfw_configure(comp_facts, vins_facts, None)
elif amodule.params['rules'] is not None:
# manage PFW rules accodring to the module arguments
pfw_facts = decon.pfw_configure(comp_facts, vins_facts, amodule.params['rules'])
else:
pfw_facts = decon._pfw_get(comp_facts['id'], vins_facts['id'])
#
# complete module run
#
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
# prepare PFW facts to be returned as part of decon.result and then call exit_json(...)
decon.result['facts'] = decon.decort_pfw_package_facts(comp_facts, vins_facts, pfw_facts, amodule.check_mode)
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main() main()

View File

@@ -102,16 +102,46 @@ class decort_rg(DecortController):
resources = self.rg_facts['Resources']['Reserved'] resources = self.rg_facts['Resources']['Reserved']
incorrect_quota = dict(Requested=dict(), incorrect_quota = dict(Requested=dict(),
Reserved=dict(),) Reserved=dict(),)
query_key_map = dict(cpu='cpu', query_key_map = dict(
ram='ram', cpu='cpu',
disk='disksize', ram='ram',
ext_ips='extips', disk='disksize',
net_transfer='exttraffic',) ext_ips='extips',
storage_policies='policies',
)
if self.amodule.params['quotas']: if self.amodule.params['quotas']:
for quota_item in self.amodule.params['quotas']: for quota_item in self.amodule.params['quotas']:
if self.amodule.params['quotas'][quota_item] < resources[query_key_map[quota_item]]: if quota_item == 'storage_policies':
incorrect_quota['Requested'][quota_item]=self.amodule.params['quotas'][quota_item] rg_storage_policies = resources[query_key_map[quota_item]]
incorrect_quota['Reserved'][quota_item]=resources[query_key_map[quota_item]] aparam_storage_policies = self.amodule.params['quotas'][
quota_item
]
for aparam_storage_policy in aparam_storage_policies:
aparam_storage_policy_id = aparam_storage_policy['id']
rg_storage_policy = rg_storage_policies.get(
str(aparam_storage_policy_id)
)
if (
rg_storage_policy
and aparam_storage_policy['storage_size_gb']
< rg_storage_policy['disksize']
):
incorrect_quota['Requested'][quota_item] = (
self.amodule.params['quotas'][quota_item]
)
incorrect_quota['Reserved'][quota_item] = (
resources[query_key_map[quota_item]]
)
elif (
self.amodule.params['quotas'][quota_item]
< 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]]
)
if incorrect_quota['Requested']: if incorrect_quota['Requested']:
self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota) self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota)
@@ -243,6 +273,7 @@ class decort_rg(DecortController):
ret_dict['uniqPools'] = self.rg_facts['uniqPools'] ret_dict['uniqPools'] = self.rg_facts['uniqPools']
ret_dict['description'] = self.rg_facts['desc'] ret_dict['description'] = self.rg_facts['desc']
ret_dict['sdn_access_group_id'] = self.rg_facts['sdn_access_group_id'] 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 ret_dict
@@ -375,73 +406,78 @@ class decort_rg(DecortController):
# 4) if RG exists: check desired state, desired configuration -> initiate action accordingly # 4) if RG exists: check desired state, desired configuration -> initiate action accordingly
# 5) report result to Ansible # 5) report result to Ansible
def main(): @DecortController.handle_sdk_exceptions
decon = decort_rg() def run(self):
amodule = decon.amodule amodule = self.amodule
#amodule.check_mode=True #amodule.check_mode=True
if decon.validated_rg_id > 0: if self.validated_rg_id > 0:
if decon.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]: if self.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]:
decon.error() self.error()
elif decon.rg_facts['status'] in ("CREATED"): elif self.rg_facts['status'] in ("CREATED"):
if amodule.params['state'] == 'absent': if amodule.params['state'] == 'absent':
decon.destroy() self.destroy()
elif amodule.params['state'] == "disabled": elif amodule.params['state'] == "disabled":
decon.enable() self.enable()
if amodule.params['state'] in ['present', 'enabled']: if amodule.params['state'] in ['present', 'enabled']:
if ( if (
amodule.params['quotas'] amodule.params['quotas']
or amodule.params['resType'] or amodule.params['resType']
or amodule.params['rename'] != "" or amodule.params['rename'] != ""
or amodule.params['sep_pools'] is not None or amodule.params['sep_pools'] is not None
or amodule.params['description'] is not None or amodule.params['description'] is not None
): ):
decon.update() self.update()
if amodule.params['access']: if amodule.params['access']:
decon.access() self.access()
if amodule.params['def_netType'] is not None: if amodule.params['def_netType'] is not None:
decon.setDefNet() self.setDefNet()
elif decon.rg_facts['status'] == "DELETED": elif self.rg_facts['status'] == "DELETED":
if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True: if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True:
decon.destroy() self.destroy()
elif (amodule.params['state'] == 'present' elif (amodule.params['state'] == 'present'
or amodule.params['state'] == 'disabled'): or amodule.params['state'] == 'disabled'):
decon.restore() self.restore()
elif amodule.params['state'] == 'enabled': elif amodule.params['state'] == 'enabled':
decon.restore() self.restore()
decon.enable() self.enable()
elif decon.rg_facts['status'] in ("DISABLED"): elif self.rg_facts['status'] in ("DISABLED"):
if amodule.params['state'] == 'absent': if amodule.params['state'] == 'absent':
decon.destroy() self.destroy()
elif amodule.params['state'] == ("enabled"): elif amodule.params['state'] == ("enabled"):
decon.enable() self.enable()
else:
if amodule.params['state'] in ('present', 'enabled'):
if not amodule.params['rg_name']:
decon.result['failed'] = True
decon.result['msg'] = (
'Resource group could not be created because'
' the "rg_name" parameter was not specified.'
)
else:
decon.create()
if amodule.params['access'] and not amodule.check_mode:
decon.access()
elif amodule.params['state'] in ('disabled'):
decon.error()
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
if decon.rg_should_exist:
if decon.result['changed']:
decon.get_info()
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)
else: else:
amodule.exit_json(**decon.result) if amodule.params['state'] in ('present', 'enabled'):
if not amodule.params['rg_name']:
self.result['failed'] = True
self.result['msg'] = (
'Resource group could not be created because'
' the "rg_name" parameter was not specified.'
)
else:
self.create()
if amodule.params['access'] and not amodule.check_mode:
self.access()
elif amodule.params['state'] in ('disabled'):
self.error()
if __name__ == "__main__":
if self.result['failed']:
amodule.fail_json(**self.result)
else:
if self.rg_should_exist:
if self.result['changed']:
self.get_info()
self.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
else:
amodule.exit_json(**self.result)
def main():
decort_rg().run()
if __name__ == '__main__':
main() main()

148
library/decort_rg_list.py Normal file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_rg_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortRGList(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(
account_id=dict(
type='int',
),
account_name=dict(
type='str',
),
created_after_timestamp=dict(
type='int',
),
created_before_timestamp=dict(
type='int',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
),
lock_status=dict(
type='str',
choices=sdk_types.LockStatus._member_names_,
),
name=dict(
type='str',
),
status=dict(
type='str',
choices=(
sdk_types.ResourceGroupStatus._member_names_
),
),
),
),
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=(
sdk_types.ResourceGroupAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_lock_status: str | None = aparam_filter['lock_status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.ResourceGroupAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.rg.list(
account_id=aparam_filter['account_id'],
account_name=aparam_filter['account_name'],
created_after_timestamp=aparam_filter['created_after_timestamp'],
created_before_timestamp=aparam_filter['created_before_timestamp'],
id=aparam_filter['id'],
include_deleted=aparam_filter['include_deleted'] or False,
lock_status=(
sdk_types.LockStatus[aparam_lock_status]
if aparam_lock_status else None
),
name=aparam_filter['name'],
status=(
sdk_types.ResourceGroupStatus[aparam_status]
if aparam_status else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortRGList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,366 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_security_group
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk import exceptions as sdk_exceptions
import dynamix_sdk.types as sdk_types
class DecortSecurityGroup(DecortController):
id: int = 0
facts: dict = dict()
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(
account_id=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
rules=dict(
type='dict',
options=dict(
mode=dict(
type='str',
choices=[
e.value
for e in self.SecurityGroupRuleMode
],
default=self.SecurityGroupRuleMode.update.value,
),
objects=dict(
type='list',
elements='dict',
required=True,
options=dict(
direction=dict(
type='str',
choices=(
sdk_types.TrafficDirection.
_member_names_
),
required=True,
),
ethertype=dict(
type='str',
choices=(
sdk_types.SGRuleEthertype.
_member_names_
),
),
id=dict(
type='int',
),
port_range=dict(
type='dict',
options=dict(
min=dict(
type='int',
),
max=dict(
type='int',
),
),
),
protocol=dict(
type='str',
choices=(
sdk_types.SGRuleProtocol._member_names_
),
),
remote_net_cidr=dict(
type='str',
),
),
),
),
),
state=dict(
type='str',
choices=[e.value for e in self.SecurityGroupState],
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
if self.aparams['id'] is not None:
self.id = self.aparams['id']
elif self.aparams['name'] is not None:
security_groups = self.api.cloudapi.security_group.list(
account_id=self.aparams['account_id'],
name=self.aparams['name'],
)
if security_groups.data:
self.id = security_groups.data[0].id
if self.id:
self.get_info()
self.check_amodule_args_for_change()
self.change()
else:
self.check_amodule_args_for_create()
self.create()
if not self.amodule.check_mode:
self.change_rules()
if self.result['changed']:
self.get_info()
self.exit()
def get_info(self):
try:
storage_policy_model = self.api.cloudapi.security_group.get(
security_group_id=self.id
)
except sdk_exceptions.RequestException as e:
if (
e.orig_exception.response
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='security_group',
id=self.id,
)
)
self.exit(fail=True)
raise e
self.facts = storage_policy_model.model_dump()
def check_amodule_args_for_create(self):
check_errors = False
aparam_account_id = self.aparams['account_id']
if aparam_account_id is None:
check_errors = True
self.message(
msg='Check for parameter "account_id" failed: '
'account_id must be specified when creating '
'a new security group'
)
aparam_name = self.aparams['name']
if aparam_name is None:
check_errors = True
self.message(
msg='Check for parameter "name" failed: '
'name must be specified when creating '
'a new security group'
)
aparam_state = self.aparams['state']
if (
aparam_state is not None
and aparam_state == self.SecurityGroupState.absent.value
):
check_errors = True
self.message(
msg='Check for parameter "state" failed: '
'state can not be "absent" when creating '
'a new security group'
)
if self.check_aparam_rules(for_create=True) is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_change(self):
check_errors = False
if self.check_aparam_rules() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_aparam_rules(self, for_create: bool = False):
check_errors = False
aparam_rules = self.aparams['rules']
if aparam_rules is None:
return True
mode = aparam_rules['mode']
rules = aparam_rules['objects']
if for_create and mode == self.SecurityGroupRuleMode.delete.value:
check_errors = True
self.message(
msg='Check for parameter "rules.mode" failed: '
'mode can not be "delete" when creating '
'new security group'
)
sg_rules_ids = []
if self.facts.get('rules'):
sg_rules_ids = [rule['id'] for rule in self.facts['rules']]
for rule in rules:
rule_id = rule.get('id')
if rule_id is not None:
if for_create:
check_errors = True
self.message(
msg='Check for parameter "rules.objects.id" failed: '
'can not set rule id when creating new '
'security group'
)
elif rule_id not in sg_rules_ids:
check_errors = True
self.message(
msg='Check for parameter "rules.objects.id" failed: '
f'rule ID {rule_id} not found for '
f'security group ID {self.id}'
)
if mode == self.SecurityGroupRuleMode.delete.value:
for param, value in rule.items():
if param != 'id' and value is not None:
check_errors = True
self.message(
msg='Check for parameter "rules.objects" '
'failed: only rule id can be specified if'
'rules.mode is "delete"'
)
break
elif mode == self.SecurityGroupRuleMode.delete.value:
check_errors = True
self.message(
msg='Check for parameter "rules.objects" '
'failed: rule id must be specified if mode is delete'
)
return not check_errors
def create(self):
id = self.sdk_checkmode(self.api.cloudapi.security_group.create)(
account_id=self.aparams['account_id'],
name=self.aparams['name'],
description=self.aparams['description'],
)
if id:
self.id = id
def change(self):
self.change_state()
self.change_params()
self.change_rules()
def change_state(self):
if self.aparams['state'] == self.SecurityGroupState.absent.value:
self.delete()
def change_params(self):
aparam_name = self.aparams['name']
aparam_description = self.aparams['description']
new_name, new_description = None, None
if (
aparam_name is not None
and aparam_name != self.facts['name']
):
new_name = aparam_name
if (
aparam_description is not None
and aparam_description != self.facts['description']
):
new_description = aparam_description
if new_name or new_description:
self.sdk_checkmode(self.api.cloudapi.security_group.update)(
security_group_id=self.id,
name=new_name,
description=new_description,
)
def change_rules(self):
aparam_rules = self.aparams['rules']
if aparam_rules is not None:
rules = aparam_rules['objects']
match aparam_rules['mode']:
case self.SecurityGroupRuleMode.delete.value:
for rule in rules:
self.security_group_detele_rule(
security_group_id=self.id,
rule_id=rule['id'],
)
case self.SecurityGroupRuleMode.match.value:
for rule in rules:
if rule.get('id') is None:
self.create_rule(rule=rule)
sg_rules_ids = set(
[rule['id'] for rule in self.facts['rules']]
)
aparam_rules_ids = set(
[rule['id'] for rule in rules if rule.get('id')]
)
rules_ids_to_delete = sg_rules_ids - aparam_rules_ids
for rule_id in rules_ids_to_delete:
self.security_group_detele_rule(
security_group_id=self.id,
rule_id=rule_id,
)
case self.SecurityGroupRuleMode.update.value:
for rule in rules:
if rule.get('id') is None:
self.create_rule(rule=rule)
def delete(self):
self.sdk_checkmode(self.api.cloudapi.security_group.delete)(
security_group_id=self.id,
)
self.facts = {}
self.exit()
def create_rule(self, rule: dict):
port_range_min, port_range_max = None, None
if rule.get('port_range'):
port_range_min = rule['port_range'].get('min')
port_range_max = rule['port_range'].get('max')
self.sdk_checkmode(self.api.cloudapi.security_group.create_rule)(
security_group_id=self.id,
traffic_direction=(
sdk_types.TrafficDirection[rule['direction']]
),
ethertype=(
sdk_types.SGRuleEthertype[rule['ethertype']]
if rule.get('ethertype') else sdk_types.SGRuleEthertype.IPV4
),
protocol=(
sdk_types.SGRuleProtocol[rule['protocol']]
if rule.get('protocol') else None
),
port_range_min=port_range_min,
port_range_max=port_range_max,
remote_net_cidr=rule.get('remote_net_cidr'),
)
def main():
DecortSecurityGroup().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,132 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_security_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortSecurityGroupList(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(
account_id=dict(
type='int',
),
created_after_timestamp=dict(
type='int',
),
created_before_timestamp=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
updated_after_timestamp=dict(
type='int',
),
updated_before_timestamp=dict(
type='int',
),
),
),
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=(
sdk_types.SecurityGroupAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.SecurityGroupAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.security_group.list(
account_id=aparam_filter['account_id'],
created_after_timestamp=aparam_filter['created_after_timestamp'],
created_before_timestamp=aparam_filter['created_before_timestamp'],
description=aparam_filter['description'],
id=aparam_filter['id'],
name=aparam_filter['name'],
updated_after_timestamp=aparam_filter['updated_after_timestamp'],
updated_before_timestamp=aparam_filter['updated_before_timestamp'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortSecurityGroupList().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,65 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_storage_policy
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk import exceptions as sdk_exceptions
class DecortStoragePolicy(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.id: int = self.aparams['id']
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
id=dict(
type='int',
required=True,
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
try:
storage_policy_model = self.api.cloudapi.storage_policy.get(
id=self.id
)
except sdk_exceptions.RequestException as e:
if (
e.orig_exception.response
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='storage_policy',
id=self.id,
)
)
self.exit(fail=True)
raise e
self.facts = storage_policy_model.model_dump()
def main():
DecortStoragePolicy().run()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,152 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_storage_policy_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortStoragePolicyList(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(
account_id=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
iops_limit=dict(
type='int',
),
name=dict(
type='str',
),
rg_id=dict(
type='int',
),
sep_id=dict(
type='int',
),
sep_pool_name=dict(
type='str',
),
status=dict(
type='str',
choices=(
sdk_types.StoragePolicyStatus._member_names_
),
),
sep_tech_status=dict(
type='str',
choices=sdk_types.SEPTechStatus._member_names_,
),
),
),
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=(
sdk_types.StoragePolicyAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_sep_tech_status: str | None = aparam_filter['sep_tech_status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.StoragePolicyAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.storage_policy.list(
account_id=aparam_filter['account_id'],
description=aparam_filter['description'],
id=aparam_filter['id'],
iops_limit=aparam_filter['iops_limit'],
name=aparam_filter['name'],
rg_id=aparam_filter['rg_id'],
sep_id=aparam_filter['sep_id'],
sep_pool_name=aparam_filter['sep_pool_name'],
status=(
sdk_types.StoragePolicyStatus[aparam_status]
if aparam_status else None
),
sep_tech_status=(
sdk_types.SEPTechStatus[aparam_sep_tech_status]
if aparam_sep_tech_status else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortStoragePolicyList().run()
if __name__ == '__main__':
main()

View File

@@ -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.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk import exceptions as sdk_exceptions
class DecortTrunk(DecortController): class DecortTrunk(DecortController):
def __init__(self): def __init__(self):
@@ -28,19 +30,31 @@ class DecortTrunk(DecortController):
supports_check_mode=True, supports_check_mode=True,
) )
@DecortController.handle_sdk_exceptions
def run(self): def run(self):
self.get_info() self.get_info()
self.exit() self.exit()
def get_info(self): def get_info(self):
self.facts = self.trunk_get(id=self.id) try:
self.facts['account_ids'] = self.facts.pop('accountIds') trunk_model = self.api.cloudapi.trunk.get(
self.facts['created_timestamp'] = self.facts.pop('created_at') id=self.id
self.facts['deleted_timestamp'] = self.facts.pop('deleted_at') )
self.facts['updated_timestamp'] = self.facts.pop('updated_at') except sdk_exceptions.RequestException as e:
self.facts['native_vlan_id'] = self.facts.pop('nativeVlanId') if (
self.facts['ovs_bridge'] = self.facts.pop('ovsBridge') e.orig_exception.response
self.facts['vlan_ids'] = self.facts.pop('trunkTags') and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='trunk',
id=self.id,
)
)
self.exit(fail=True)
raise e
self.facts = trunk_model.model_dump()
def main(): def main():

View File

@@ -0,0 +1,123 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_trunk_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortTrunkList(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(
account_ids=dict(
type='list',
elements='int',
),
ids=dict(
type='list',
elements='int',
),
status=dict(
type='str',
choices=sdk_types.TrunkStatus._member_names_,
),
vlan_ids=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=(
sdk_types.TrunkAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.TrunkAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.trunk.list(
account_ids=aparam_filter['account_ids'],
ids=aparam_filter['ids'],
status=(
sdk_types.TrunkStatus[aparam_status]
if aparam_status else None
),
vlan_ids=aparam_filter['vlan_ids'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortTrunkList().run()
if __name__ == '__main__':
main()

68
library/decort_user.py Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_user
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortUser(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(
api_methods=dict(
type='bool',
default=False,
),
objects_search=dict(
type='str',
),
resource_consumption=dict(
type='bool',
default=False,
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
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]
if self.aparams['resource_consumption']:
self.facts.update(self.user_resource_consumption())
if self.aparams['api_methods']:
self.facts['api_methods'] = self.user_api_methods(id=self.id)
search_string = self.aparams['objects_search']
if search_string:
self.facts['objects_search'] = self.user_objects_search(
search_string=search_string,
)
def main():
DecortUser().run()
if __name__ == '__main__':
main()

View File

@@ -8,607 +8,37 @@ description: See L(Module Documentation,https://repository.basistech.ru/BASIS/de
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortUserInfo(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.check_amodule_args()
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
argument_spec=dict(
accounts=dict(
type='dict',
options=dict(
deleted=dict(
type='bool',
default=False,
),
filter=dict(
type='dict',
options=dict(
rights=dict(
type='str',
choices=[
e.value for e in self.AccountUserRights
],
),
id=dict(
type='int',
),
name=dict(
type='str',
),
status=dict(
type='str',
choices=[
e.value for e in self.AccountStatus
],
),
),
),
pagination=dict(
type='dict',
options=dict(
number=dict(
type='int',
default=1,
),
size=dict(
type='int',
required=True,
),
),
),
resource_consumption=dict(
type='bool',
default=False,
),
sorting=dict(
type='dict',
options=dict(
asc=dict(
type='bool',
default=True,
),
field=dict(
type='str',
choices=[
e.value
for e in self.AccountSortableField
],
required=True,
),
),
),
),
),
api_methods=dict(
type='bool',
default=False,
),
audits=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
api_method=dict(
type='str',
),
status_code=dict(
type='dict',
options=dict(
min=dict(
type='int',
),
max=dict(
type='int',
),
),
),
time=dict(
type='dict',
options=dict(
start=dict(
type='dict',
options=dict(
timestamp=dict(
type='int',
),
datetime=dict(
type='str',
),
),
mutually_exclusive=[
('timestamp', 'datetime'),
],
),
end=dict(
type='dict',
options=dict(
timestamp=dict(
type='int',
),
datetime=dict(
type='str',
),
),
mutually_exclusive=[
('timestamp', 'datetime'),
],
),
),
),
),
),
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=[
e.value
for e in self.AuditsSortableField
],
required=True,
),
),
),
),
),
objects_search=dict(
type='str',
),
resource_consumption=dict(
type='bool',
default=False,
),
zones=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
deletable=dict(
type='bool',
),
description=dict(
type='str',
),
grid_id=dict(
type='int',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
node_id=dict(
type='int',
),
status=dict(
type='str',
choices=[
e.value for e in self.ZoneStatus
],
),
),
),
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=self.ZoneField._member_names_,
required=True,
),
),
),
),
),
trunks=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
ids=dict(
type='list',
),
account_ids=dict(
type='list',
),
status=dict(
type='str',
choices=[
e.value for e in self.TrunkStatus
],
),
vlan_ids=dict(
type='list',
),
),
),
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=[
e.value
for e in self.TrunksSortableField
],
required=True,
),
),
),
),
),
),
supports_check_mode=True,
)
def check_amodule_args(self):
"""
Additional validation of Ansible Module arguments.
This validation cannot be implemented using
Ansible Argument spec.
"""
check_error = False
match self.aparams['audits']:
case {
'filter': {'time': {'start': {'datetime': str() as dt_str}}}
}:
if self.dt_str_to_sec(dt_str=dt_str) is None:
self.message(self.MESSAGES.str_not_parsed(string=dt_str))
check_error = True
match self.aparams['audits']:
case {
'filter': {'time': {'end': {'datetime': str() as dt_str}}}
}:
if self.dt_str_to_sec(dt_str=dt_str) is None:
self.message(self.MESSAGES.str_not_parsed(string=dt_str))
check_error = True
aparam_trunks = self.aparams['trunks']
if (
aparam_trunks is not None
and aparam_trunks['filter'] is not None
and aparam_trunks['filter']['vlan_ids'] is not None
):
for vlan_id in aparam_trunks['filter']['vlan_ids']:
if not (
self.TRUNK_VLAN_ID_MIN_VALUE
<= vlan_id
<= self.TRUNK_VLAN_ID_MAX_VALUE
):
check_error = True
self.message(
'Check for parameter "trunks.filter.vlan_ids" failed: '
f'VLAN ID {vlan_id} must be in range 1-4095.'
)
if check_error:
self.exit(fail=True)
@property
def mapped_accounts_args(self) -> None | dict:
"""
Map the module argument `accounts` to
arguments dictionary for the method
`DecortController.user_accounts`.
"""
input_args = self.aparams['accounts']
if not input_args:
return input_args
mapped_args = {}
mapped_args['deleted'] = input_args['deleted']
mapped_args['resource_consumption'] = (
input_args['resource_consumption']
)
input_args_filter = input_args['filter']
if input_args_filter:
input_args_filter_rights = input_args_filter['rights']
if input_args_filter_rights:
mapped_args['account_user_rights'] = (
self.AccountUserRights(input_args_filter_rights)
)
mapped_args['account_id'] = input_args_filter['id']
mapped_args['account_name'] = input_args_filter['name']
input_args_filter_status = input_args_filter['status']
if input_args_filter_status:
mapped_args['account_status'] = (
self.AccountStatus(input_args_filter_status)
)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.AccountSortableField(input_args_sorting_field)
)
return mapped_args
@property
def mapped_audits_args(self):
"""
Map the module argument `audits` to
arguments dictionary for the method
`DecortController.user_audits`.
"""
input_args = self.aparams['audits']
if not input_args:
return input_args
mapped_args = {}
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args['api_method'] = input_args_filter['api_method']
match input_args_filter['status_code']:
case {'min': int() as min_status_code}:
mapped_args['min_status_code'] = min_status_code
match input_args_filter['status_code']:
case {'max': int() as max_status_code}:
mapped_args['max_status_code'] = max_status_code
match input_args_filter['time']:
case {'start': {'timestamp': int() as start_unix_time}}:
mapped_args['start_unix_time'] = start_unix_time
case {'start': {'datetime': str() as start_dt_str}}:
mapped_args['start_unix_time'] = self.dt_str_to_sec(
dt_str=start_dt_str
)
match input_args_filter['time']:
case {'end': {'timestamp': int() as end_unix_time}}:
mapped_args['end_unix_time'] = end_unix_time
case {'end': {'datetime': str() as end_dt_str}}:
mapped_args['end_unix_time'] = self.dt_str_to_sec(
dt_str=end_dt_str
)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.AuditsSortableField(input_args_sorting_field)
)
return mapped_args
@property
def mapped_zones_args(self):
"""
Map the module argument `zones` to
arguments dictionary for the method
`DecortController.user_zones`.
"""
input_args = self.aparams['zones']
if not input_args:
return input_args
mapped_args = {}
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args.update(input_args_filter)
input_args_filter_status = input_args_filter['status']
if input_args_filter_status:
mapped_args['status'] = (
self.ZoneStatus(input_args_filter_status)
)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.ZoneField._member_map_[input_args_sorting_field]
)
return mapped_args
@property
def mapped_trunks_args(self):
"""
Map the module argument `trunks` to
arguments dictionary for the method
`DecortController.user_trunks`.
"""
input_args = self.aparams['trunks']
if not input_args:
return input_args
mapped_args = {}
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args.update(input_args_filter)
input_args_filter_status = input_args_filter['status']
if input_args_filter_status:
mapped_args['status'] = (
self.TrunkStatus(input_args_filter_status)
)
input_args_filter_vlan_ids = input_args_filter['vlan_ids']
if input_args_filter_vlan_ids is not None:
mapped_args['vlan_ids'] = ', '.join(
map(str, input_args_filter_vlan_ids)
)
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
mapped_args['page_size'] = input_args_pagination['size']
input_args_sorting = input_args['sorting']
if input_args_sorting:
mapped_args['sort_by_asc'] = input_args_sorting['asc']
input_args_sorting_field = input_args_sorting['field']
if input_args_sorting_field:
mapped_args['sort_by_field'] = (
self.TrunksSortableField(input_args_sorting_field)
)
return mapped_args
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.facts = self.user_whoami()
self.id = self.facts['name']
user_get = self.user_get(id=self.id)
for key in ['emailaddresses', 'data']:
self.facts[key] = user_get[key]
if self.aparams['accounts']:
self.facts['accounts'] = self.user_accounts(
**self.mapped_accounts_args,
)
if self.aparams['resource_consumption']:
self.facts.update(self.user_resource_consumption())
if self.aparams['audits']:
self.facts['audits'] = self.user_audits(**self.mapped_audits_args)
if self.aparams['api_methods']:
self.facts['api_methods'] = self.user_api_methods(id=self.id)
search_string = self.aparams['objects_search']
if search_string:
self.facts['objects_search'] = self.user_objects_search(
search_string=search_string,
)
if self.aparams['zones']:
self.facts['zones'] = self.user_zones(**self.mapped_zones_args)
if self.aparams['trunks']:
self.facts['trunks'] = self.user_trunks(**self.mapped_trunks_args)
for trunk_facts in self.facts['trunks']:
trunk_facts['account_ids'] = trunk_facts.pop('accountIds')
trunk_facts['created_timestamp'] = trunk_facts.pop(
'created_at'
)
trunk_facts['deleted_timestamp'] = trunk_facts.pop(
'deleted_at'
)
trunk_facts['updated_timestamp'] = trunk_facts.pop(
'updated_at'
)
trunk_facts['native_vlan_id'] = trunk_facts.pop(
'nativeVlanId'
)
trunk_facts['ovs_bridge'] = trunk_facts.pop('ovsBridge')
trunk_facts['vlan_ids'] = trunk_facts.pop('trunkTags')
def main(): def main():
DecortUserInfo().run() module = AnsibleModule(
argument_spec=dict(
app_id=dict(type='raw'),
app_secret=dict(type='raw'),
authenticator=dict(type='raw'),
controller_url=dict(type='raw'),
domain=dict(type='raw'),
jwt=dict(type='raw'),
oauth2_url=dict(type='raw'),
password=dict(type='raw'),
username=dict(type='raw'),
verify_ssl=dict(type='raw'),
ignore_api_compatibility=dict(type='raw'),
ignore_sdk_version_check=dict(type='raw'),
api_methods=dict(type='raw'),
objects_search=dict(type='raw'),
resource_consumption=dict(type='raw'),
),
supports_check_mode=True,
)
module.fail_json(
msg=(
'The module "decort_user_info" has been renamed to "decort_user". '
'Please update your playbook to use "decort_user" '
'instead of "decort_user_info".'
),
)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -170,7 +170,7 @@ class decort_vins(DecortController):
return return
def delete(self): def delete(self):
self.vins_delete(self.vins_id, permanently=True) self.vins_delete(self.vins_id, self.amodule.params['permanently'])
self.vins_facts['status'] = 'DESTROYED' self.vins_facts['status'] = 'DESTROYED'
return return
def nop(self): def nop(self):
@@ -320,6 +320,10 @@ class decort_vins(DecortController):
type='str', type='str',
default='', default='',
), ),
permanently=dict(
type='bool',
default=False,
),
vins_id=dict( vins_id=dict(
type='int', type='int',
default=0, default=0,
@@ -361,101 +365,106 @@ class decort_vins(DecortController):
# 4) if ViNS exists: check desired state, desired configuration -> initiate action(s) accordingly # 4) if ViNS exists: check desired state, desired configuration -> initiate action(s) accordingly
# 5) report result to Ansible # 5) report result to Ansible
def main(): @DecortController.handle_sdk_exceptions
decon = decort_vins() def run(self):
amodule = decon.amodule amodule = self.amodule
# #
# Initial validation of module arguments is complete # Initial validation of module arguments is complete
# #
# At this point non-zero vins_id means that we will be managing pre-existing ViNS # At this point non-zero vins_id means that we will be managing pre-existing ViNS
# Otherwise we are about to create a new one as follows: # Otherwise we are about to create a new one as follows:
# - if validated_rg_id is non-zero, create ViNS @ RG level # - if validated_rg_id is non-zero, create ViNS @ RG level
# - if validated_rg_id is zero, create ViNS @ account level # - if validated_rg_id is zero, create ViNS @ account level
# #
# When managing existing ViNS we need to account for both "static" and "transient" # When managing existing ViNS we need to account for both "static" and "transient"
# status. Full range of ViNS statii is as follows: # status. Full range of ViNS statii is as follows:
# #
# "MODELED", "CREATED", "ENABLED", "ENABLING", "DISABLED", "DISABLING", "DELETED", "DELETING", "DESTROYED", "DESTROYING" # "MODELED", "CREATED", "ENABLED", "ENABLING", "DISABLED", "DISABLING", "DELETED", "DELETING", "DESTROYED", "DESTROYING"
# #
# if cconfig_save is true, only config save without other updates # if cconfig_save is true, only config save without other updates
vins_should_exist = False vins_should_exist = False
if decon.vins_id: if self.vins_id:
vins_should_exist = True
if decon.vins_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING"]:
# error: nothing can be done to existing ViNS in the listed statii regardless of
# the requested state
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("No change can be done for existing ViNS ID {} because of its current "
"status '{}'").format(decon.vins_id, decon.vins_facts['status'])
elif decon.vins_facts['status'] == "DISABLED":
if amodule.params['state'] == 'absent':
decon.delete()
vins_should_exist = False
elif amodule.params['state'] in ('present', 'disabled'):
# update ViNS, leave in disabled state
decon.action()
elif amodule.params['state'] == 'enabled':
# update ViNS and enable
decon.action('enabled')
elif decon.vins_facts['status'] in ["CREATED", "ENABLED"]:
if amodule.params['state'] == 'absent':
decon.delete()
vins_should_exist = False
elif amodule.params['state'] in ('present', 'enabled'):
# update ViNS
decon.action()
elif amodule.params['state'] == 'disabled':
# disable and update ViNS
decon.action('disabled')
elif decon.vins_facts['status'] == "DELETED":
if amodule.params['state'] in ['present', 'enabled']:
# restore and enable
decon.action(restore=True)
vins_should_exist = True
elif amodule.params['state'] == 'absent':
# destroy permanently
decon.delete()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
decon.error()
vins_should_exist = False
elif decon.vins_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
# need to re-provision ViNS;
decon.create()
vins_should_exist = True
elif amodule.params['state'] == 'absent':
decon.nop()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
decon.error()
else:
# 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':
decon.nop()
elif amodule.params['state'] in ('present', 'enabled'):
decon.check_amodule_argument('vins_name')
# as we already have account ID and RG ID we can create ViNS and get vins_id on success
decon.create()
vins_should_exist = True vins_should_exist = True
elif amodule.params['state'] == 'disabled': if self.vins_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING"]:
decon.error() # error: nothing can be done to existing ViNS in the listed statii regardless of
# # the requested state
# conditional switch end - complete module run self.result['failed'] = True
# self.result['changed'] = False
if decon.result['failed']: self.result['msg'] = ("No change can be done for existing ViNS ID {} because of its current "
amodule.fail_json(**decon.result) "status '{}'").format(self.vins_id, self.vins_facts['status'])
else: elif self.vins_facts['status'] == "DISABLED":
# prepare ViNS facts to be returned as part of decon.result and then call exit_json(...) if amodule.params['state'] == 'absent':
if decon.result['changed']: self.delete()
_, decon.vins_facts = decon.vins_find(decon.vins_id) vins_should_exist = False
decon.result['facts'] = decon.package_facts(amodule.check_mode) elif amodule.params['state'] in ('present', 'disabled'):
amodule.exit_json(**decon.result) # 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"]:
if amodule.params['state'] == 'absent':
self.delete()
vins_should_exist = False
elif 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":
if amodule.params['state'] in ['present', 'enabled']:
# restore and enable
self.action(restore=True)
vins_should_exist = True
elif amodule.params['state'] == 'absent':
# destroy permanently
if self.amodule.params['permanently']:
self.delete()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
self.error()
vins_should_exist = False
elif self.vins_facts['status'] == "DESTROYED":
if amodule.params['state'] in ('present', 'enabled'):
# need to re-provision ViNS;
self.create()
vins_should_exist = True
elif amodule.params['state'] == 'absent':
self.nop()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
self.error()
else:
# 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':
self.nop()
elif amodule.params['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':
self.error()
#
# conditional switch end - complete module run
#
if self.result['failed']:
amodule.fail_json(**self.result)
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.result['facts'] = self.package_facts(amodule.check_mode)
amodule.exit_json(**self.result)
if __name__ == "__main__": def main():
decort_vins().run()
if __name__ == '__main__':
main() main()

141
library/decort_vins_list.py Normal file
View File

@@ -0,0 +1,141 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vins_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortVINSList(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(
account_id=dict(
type='int',
),
ext_net_ip=dict(
type='str',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
),
name=dict(
type='str',
),
rg_id=dict(
type='int',
),
status=dict(
type='str',
choices=sdk_types.VINSStatus._member_names_,
),
vnfdev_id=dict(
type='int',
),
zone_id=dict(
type='int',
),
),
),
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=(
sdk_types.VINSForListAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.VINSForListAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.vins.list(
account_id=aparam_filter['account_id'],
ext_net_ip=aparam_filter['ext_net_ip'],
id=aparam_filter['id'],
include_deleted=aparam_filter['include_deleted'] or False,
name=aparam_filter['name'],
rg_id=aparam_filter['rg_id'],
status=(
sdk_types.VINSStatus[aparam_status]
if aparam_status else None
),
vnfdev_id=aparam_filter['vnfdev_id'],
zone_id=aparam_filter['zone_id'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortVINSList().run()
if __name__ == '__main__':
main()

2510
library/decort_vm.py Normal file

File diff suppressed because it is too large Load Diff

158
library/decort_vm_list.py Normal file
View File

@@ -0,0 +1,158 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_vm_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortVMList(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(
account_id=dict(
type='int',
),
ext_net_id=dict(
type='int',
),
ext_net_name=dict(
type='str',
),
id=dict(
type='int',
),
include_deleted=dict(
type='bool',
),
ip_addr=dict(
type='str',
),
name=dict(
type='str',
),
rg_id=dict(
type='int',
),
rg_name=dict(
type='str',
),
status=dict(
type='str',
choices=sdk_types.VMStatus._member_names_,
),
tech_status=dict(
type='str',
choices=sdk_types.VMTechStatus._member_names_,
),
zone_id=dict(
type='int',
),
),
),
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=(
sdk_types.VMAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_tech_status: str | None = aparam_filter['tech_status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.VMAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.compute.list(
account_id=aparam_filter['account_id'],
ext_net_id=aparam_filter['ext_net_id'],
ext_net_name=aparam_filter['ext_net_name'],
id=aparam_filter['id'],
include_deleted=aparam_filter['include_deleted'] or False,
ip_addr=aparam_filter['ip_addr'],
name=aparam_filter['name'],
rg_id=aparam_filter['rg_id'],
rg_name=aparam_filter['rg_name'],
status=(
sdk_types.VMStatus[aparam_status]
if aparam_status else None
),
tech_status=(
sdk_types.VMTechStatus[aparam_tech_status]
if aparam_tech_status else None
),
zone_id=aparam_filter['zone_id'],
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortVMList().run()
if __name__ == '__main__':
main()

View File

@@ -2,7 +2,7 @@
DOCUMENTATION = r''' DOCUMENTATION = r'''
--- ---
module: decort_snapshot module: decort_vm_snapshot
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501 description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
''' '''
@@ -12,7 +12,7 @@ from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController from ansible.module_utils.decort_utils import DecortController
class DecortSnapshot(DecortController): class DecortVMSnapshot(DecortController):
def __init__(self): def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args)) super().__init__(AnsibleModule(**self.amodule_init_args))
self.check_amodule_args() self.check_amodule_args()
@@ -30,9 +30,10 @@ class DecortSnapshot(DecortController):
self.exit(fail=True) self.exit(fail=True)
self.vm_name = self.vm_facts['name'] self.vm_name = self.vm_facts['name']
self.vm_snapshots = self.vm_facts['snapSets'] self.vm_snapshots = self.api.ca.compute.snapshot_list(
vm_id=self.vm_id).data
self.vm_snapshot_labels = [ self.vm_snapshot_labels = [
snapshot['label'] for snapshot in self.vm_snapshots snapshot.label for snapshot in self.vm_snapshots
] ]
self.new_snapshot_label = None self.new_snapshot_label = None
@@ -102,21 +103,23 @@ class DecortSnapshot(DecortController):
if check_error: if check_error:
self.exit(fail=True) self.exit(fail=True)
@DecortController.handle_sdk_exceptions
def run(self): def run(self):
self.get_info(first_run=True) self.get_info()
self.check_amodule_args_for_change() self.check_amodule_args_for_change()
self.change() self.change()
self.exit() self.exit()
def get_info(self, first_run: bool = False): def get_info(self, update_vm_snapshots: bool = False):
if not first_run: if update_vm_snapshots:
self.vm_snapshots = self.snapshot_list( self.vm_snapshots = self.api.cloudapi.compute.snapshot_list(
compute_id=self.aparams_vm_id, vm_id=self.aparams_vm_id,
) ).data
label = self.new_snapshot_label or self.aparams_label label = self.new_snapshot_label or self.aparams_label
for snapshot in self.vm_snapshots: for snapshot in self.vm_snapshots:
if snapshot['label'] == label: if snapshot.label == label:
self.facts = snapshot self.facts = snapshot.model_dump()
if self.aparams['usage']: if self.aparams['usage']:
self.facts['stored'] = self.get_snapshot_usage() self.facts['stored'] = self.get_snapshot_usage()
self.facts['vm_id'] = self.aparams_vm_id self.facts['vm_id'] = self.aparams_vm_id
@@ -134,11 +137,11 @@ class DecortSnapshot(DecortController):
self.abort_merge() self.abort_merge()
def create(self): def create(self):
self.snapshot_create( self.sdk_checkmode(self.api.cloudapi.compute.snapshot_create)(
compute_id=self.aparams_vm_id, vm_id=self.aparams_vm_id,
label=self.new_snapshot_label, label=self.new_snapshot_label,
) )
self.get_info() self.get_info(update_vm_snapshots=True)
def delete(self): def delete(self):
self.snapshot_delete( self.snapshot_delete(
@@ -171,7 +174,7 @@ class DecortSnapshot(DecortController):
): ):
check_errors = True check_errors = True
self.message( self.message(
f'Check for parameter "state" failed: ' 'Check for parameter "state" failed: '
'Merge can be aborted only for VM in "MERGE" tech status.' 'Merge can be aborted only for VM in "MERGE" tech status.'
) )
@@ -179,9 +182,8 @@ class DecortSnapshot(DecortController):
self.exit(fail=True) self.exit(fail=True)
def main(): def main():
DecortSnapshot().run() DecortVMSnapshot().run()
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -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.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController from ansible.module_utils.decort_utils import DecortController
from dynamix_sdk import exceptions as sdk_exceptions
class DecortZone(DecortController): class DecortZone(DecortController):
def __init__(self): def __init__(self):
@@ -28,16 +30,29 @@ class DecortZone(DecortController):
supports_check_mode=True, supports_check_mode=True,
) )
@DecortController.handle_sdk_exceptions
def run(self): def run(self):
self.get_info() self.get_info()
self.exit() self.exit()
def get_info(self): def get_info(self):
self.facts = self.zone_get(id=self.id) try:
self.facts['grid_id'] = self.facts.pop('gid') zone_model = self.api.cloudapi.zone.get(id=self.id)
self.facts['created_timestamp'] = self.facts.pop('createdTime') except sdk_exceptions.RequestException as e:
self.facts['updated_timestamp'] = self.facts.pop('updatedTime') if (
self.facts['node_ids'] = self.facts.pop('nodeIds') e.orig_exception.response
and e.orig_exception.response.status_code == 404
):
self.message(
self.MESSAGES.obj_not_found(
obj='zone',
id=self.id,
)
)
self.exit(fail=True)
raise e
self.facts = zone_model.model_dump()
def main(): def main():

133
library/decort_zone_list.py Normal file
View File

@@ -0,0 +1,133 @@
#!/usr/bin/python
DOCUMENTATION = r'''
---
module: decort_zone_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
from dynamix_sdk.base import get_alias, name_mapping_dict
import dynamix_sdk.types as sdk_types
class DecortZoneList(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(
deletable=dict(
type='bool',
),
description=dict(
type='str',
),
grid_id=dict(
type='int',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
node_id=dict(
type='int',
),
status=dict(
type='str',
choices=sdk_types.ZoneStatus._member_names_,
),
),
),
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=(
sdk_types.ZoneForListAPIResultNM
.model_fields.keys()
),
required=True,
),
),
),
),
supports_check_mode=True,
)
@DecortController.handle_sdk_exceptions
def run(self):
self.get_info()
self.exit()
def get_info(self):
aparam_filter: dict[str, Any] = self.aparams['filter']
aparam_status: str | None = aparam_filter['status']
aparam_pagination: dict[str, Any] = self.aparams['pagination']
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
sort_by: str | None = None
if aparam_sorting:
sorting_field = get_alias(
field_name=aparam_sorting['field'],
model_cls=sdk_types.ZoneForListAPIResultNM,
name_mapping_dict=name_mapping_dict,
)
sort_by_prefix = '+' if aparam_sorting['asc'] else '-'
sort_by = f'{sort_by_prefix}{sorting_field}'
self.facts = self.api.cloudapi.zone.list(
deletable=aparam_filter['deletable'],
description=aparam_filter['description'],
grid_id=aparam_filter['grid_id'],
id=aparam_filter['id'],
name=aparam_filter['name'],
node_id=aparam_filter['node_id'],
status=(
sdk_types.ZoneStatus[aparam_status]
if aparam_status else None
),
page_number=aparam_pagination['number'],
page_size=aparam_pagination['size'],
sort_by=sort_by,
).model_dump()['data']
def main():
DecortZoneList().run()
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
-r requirements.txt
pre-commit==4.1.0

View File

@@ -1,2 +1,3 @@
ansible==11.6.0 ansible==11.6.0
requests==2.32.3 requests==2.32.3
git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@1.4.latest