main 10.0.0
sskarimov 3 weeks ago
parent 06336697a6
commit becbe65993

@ -1,122 +1,174 @@
# Список изменений в версии 9.0.0 # Список изменений в версии 10.0.0
## Добавлено ## Добавлено
### Глобально ### Модуль decort_vm_snapshot
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-798 | Обновлены системные требования: версия интерпретатора Python обновлена до 3.12, версия Python-библиотеки ansible обновлена до 11.6.0 | | BANS-807 | Модуль переименован из `decort_snapshot` в `decort_vm_snapshot`. |
### Модуль decort_kvmvm ### Модуль decort_kvmvm
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-790 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. | | BANS-723 | Добавлен параметр `cdrom` и возвращаемое значение `cd_image_id`. |
| BANS-810 | Добавлен параметр `guest_agent` и возвращаемое значение `guest_agent`. | | BANS-727 | Добавлен параметр `boot.from_cdrom`. |
| BANS-806 | Добавлен параметр `get_snapshot_merge_status` и возвращаемое значение `snapshot_merge_status`. | | BANS-728 | Добавлен параметр `boot.order` и возвращаемое значение `boot_order`. |
| BANS-823 | Добавлено значение `TRUNK` для параметра `networks.type`. | | BANS-871 | Добавлен параметр `storage_policy_id`. |
| BANS-813 | Добавлено значение `SDN` для параметра `networks.type`. | | BANS-729 | Добавлен параметр `boot.disk_redeploy`. |
| BANS-835 | Добавлена возможность использования параметра `networks.mtu` для внешней сети. | | BANS-693 | Добавлена проверка при подключении сетей к запущенной ВМ. |
| BANS-881 | Добавлены параметры `networks.security_group_mode` и `networks.security_group_ids` и возвращаемые значения `interfaces.security_group_mode` и `interfaces.security_group_ids`. |
| BANS-895 | Добавлен возможный тип данных `null` для возвращаемого значения `image_id`. |
| BANS-894 | Добавлен параметр `os_version` и возвращаемое значение `os_version`. |
| BANS-890 | Добавлен параметр `networks.enabled` и возвращаемое значение `interfaces.enabled`. |
| BANS-902 | Добавлено возвращаемое значение `boot_loader_metaiso`. |
| BANS-904 | Добавлены параметры `clone_from.sep_pool_name`, `clone_from.sep_id`, `clone_from.storage_policy_id`. |
| BANS-905 | Добавлены параметры `abort_cloning`, `get_cloning_status` и возвращаемое значение `cloning_status`. |
### Модуль decort_lb ### Модуль decort_group
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-793 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. | | BANS-865 | Добавлен параметр `storage_policy_id`. |
| BANS-819 | Добавлено возвращаемое значение `account_id`. |
| BANS-800 | Добавлены значения `stopped` и `started` для параметра `state` и возвращаемое значение `tech_status`. |
### Модуль decort_k8s ### Модуль decort_disk
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-794 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. | | BANS-867 | Добавлен параметр `storage_policy_id` и возвращаемое значение `storage_policy_id`. |
| BANS-804 | Добавлены значения `stopped` и `started` для параметра `state`. | | BANS-866 | Добавлена возможность изменять `storage_policy_id` диска. |
| BANS-897 | Добавлено возвращаемое значение `to_clean`. |
| BANS-898 | Добавлены параметры `disks.objects`, `disks.objects.pci_slot_num_hex`, `disks.objects.bus_num_hex`. |
### Модуль decort_vins ### Модуль decort_osimage
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-791 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. | | BANS-868 | Добавлен параметр `storage_policy_id` и возвращаемое значение `storage_policy_id`. |
| BANS-885 | Добавлена возможность изменять `storage_policy_id` шаблонного образа. |
| BANS-914 | Добавлено возвращаемое значение `to_clean`. |
### Модуль decort_bservice ### Модуль decort_k8s
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-792 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. | | BANS-869 | Добавлен параметр `storage_policy_id`. |
| BANS-805 | Добавлены значения `stopped` и `started` для параметра `state`. | | BANS-234 | Добавлена возможность переименования кластера. |
### Модуль decort_user_info ### Модуль decort_rg
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-796 | Добавлен параметр `zones` и возвращаемое значение `zones`. | | BANS-877 | Добавлено возвращаемое значение `storage_policy_ids`. |
| BANS-826 | Добавлен параметр `trunks` и возвращаемое значение `trunks`. | | BANS-882 | Добавлен параметр `quotas.storage_policies`. |
### Модуль decort_account ### Модуль decort_account
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-789 | Добавлен параметр `default_zone_id` и возвращаемые значение `zoneIds`, `defaultZoneId`. | | BANS-875 | Добавлены возвращаемые значения `storage_policy_ids` и `resourceLimits.storage_policies`. |
### Модуль decort_account_info ### Модуль decort_account_info
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-809 | Добавлено значение `MERGE` для параметра `computes.filter.tech_status`. | | BANS-876 | Добавлены возвращаемые значения `storage_policy_ids` и `resourceLimits.storage_policies`. |
| BANS-855 | Добавлены значения `SNAPCREATE`, `CLONING`, `ROLLBACK` для параметра `computes.filter.tech_status`. | | BANS-911 | У параметра `computes.filter.tech_status` добавлены значения `MIGRATING_IN` и `MIGRATING_OUT`. |
| BANS-903 | Добавлены новые возвращаемые значения в `audits`. |
### Модуль decort_rg ### Модуль decort_storage_policy
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-812 | Добавлен параметр `sdn_access_group_id` и возвращаемое значение `sdn_access_group_id`. | | BANS-878 | Добавлен модуль `decort_storage_policy`. |
### Модуль decort_zone ### Модуль decort_user_info
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-795 | Добавлен модуль `decort_zone` для получения информации о зонах. | | BANS-879 | Добавлен параметр `storage_policies` и возвращаемое значение `storage_policies`. |
| BANS-889 | Добавлен параметр `accounts.filter.zone_id`. |
| BANS-892 | Добавлено возвращаемое значение `accounts.zone_ids`. |
| BANS-891 | Добавлен параметр `security_groups` и возвращаемое значение `security_groups`. |
### Модуль decort_trunk ### Модуль decort_security_group
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-825 | Добавлен модуль `decort_trunk` для получения информации о транковых портах. | | BANS-883 | Добавлен модуль `decort_security_group`. |
### Модуль decort_snapshot ### Модуль decort_vins
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-808 | Добавлено значение `merge_aborted` для параметра `state`. | | BANS-147 | Добавлен параметр `permanently` для удаления внутренней сети в корзину или безвозвратно. |
## Удалено
### Модуль decort_osimage ### Модуль decort_osimage
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-849 | Добавлен параметр `account_id`, используемый при создании шаблонных и виртуальных образов. | | BANS-862 | Удален параметр `drivers`. |
| BANS-292 | Удален неиспользуемый параметр `gid`. |
### Модуль decort_kvmvm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-730 | Удалён параметр `image_name`. |
| BANS-893 | Удалена необходимость указывать параметр `networks.mac` при подключении сети `SDN`. |
## Удалено
### Модуль decort_disk ### Модуль decort_disk
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-815 | Удалено значение по умолчанию для параметра `description`. | | BANS-867 | Удалён параметр `iops`. |
| BANS-898 | Удалён параметр `disks.ids`. |
### Модуль decort_lb ### Модуль decort_lb
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-815 | Удалено значение по умолчанию для параметра `description`. | | BANS-320 | Удалён неиспользуемый параметр `ext_ip_addr`. |
### Модуль decort_k8s ### Модуль decort_k8s
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-804 | Удален параметр `started` в связи с переносом логики в параметр `state` (значение `started`). | | BANS-199 | Удалён неиспользуемый параметр `quotas`. |
### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-388 | Удалён неиспользуемый параметр `image_name`. |
## Исправлено
### Модуль decort_bservice ### Модуль decort_bservice
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-805 | Удален параметр `started` в связи с переносом логики в параметр `state` (значение `started`). | | BANS-851 | Скорректирована логика параметра целевого состояния `present`. Теперь состояние `present` соответствует тому, что базовая служба существует, и не приводит к изменению состояния существующей базовой службы. Также для параметра `state` значение по умолчанию `present` теперь только при создании базовой службы. |
| BANS-873 | Указание параметров `account_id`/`account_name`, `rg_name`, `name` могло найти базовую службу с таким же именем, но принадлежащей другому аккаунту.|
### Модуль decort_osimage ### Модуль decort_kvmvm
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-849 | Удален параметр `account_Id` в связи с переименованием в `account_id`. | | BANS-685 | Исправлена ошибка, приводящая к отображению неактуальной информации о виртуальной машине после её удаления. |
| BANS-699 | Исправлена логика удаления ВМ в статусе DISABLED, приводящая к возникновению ошибок. |
| BANS-748 | Исправлена логика работы с несуществующей ВМ, по которой получение информации о ВМ приводило к возникновению ошибок. |
| BANS-697 | Исправлена логика, приводящая к ошибке при указании идентификатора несуществующего образа. |
| BANS-122 | Исправлена логика, приводящая к изменению `affinity rules`, `anti-affinity rules`, `anti-affinity rules` и ` tags` в режиме `check_mode`. |
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-742 | Исправлена ошибка, приводящая к отображению неактуальной информации о статусе диска после его удаления. |
| BANS-743 | Исправлена логика работы с несуществующим диском, по которой получение информации о диске приводило к возникновению ошибок. |
| BANS-247 | Модуль завершал свою работу ошибкой запроса API при создании/изменении диска с указанием параметра `size` меньше 1. |
## Исправлено
### Модуль decort_lb ### Модуль decort_lb
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-803 | Модуль завершал работу ошибкой Python при создании балансировщика с указанием параметра `backends` или `frontends`. | | BANS-752 | Исправлена ошибка, приводящая к отображению неактуальной информации о статусе балансировщика после его удаления. |
| BANS-820 | Выполнение модуля с указанием параметра `vins_id` и без указания параметра `ext_net_id` вызывало создание балансировщика с некорректной сетевой конфигурацией, дальнейшее добавление конфигурации backend к которому завершалось ошибкой платформы. | | BANS-754 | Исправлена логика работы с несуществующими объектами, по которой получение информации об объектах приводило к возникновению ошибок. |
| BANS-799 | Скорректирована логика параметра целевого состояния `present`. Теперь состояние `present` соответствует тому, что балансировщик нагрузки существует, и не приводит к изменению состояния существующего балансировщика нагрузки. Также для параметра `state` значение по умолчанию `present` теперь только при создании балансировщика нагрузки. | | BANS-753 | Исправлена логика работы с несуществующим балансировщиком, по которой получение информации о балансировщике приводило к возникновению ошибок. |
### Модуль decort_account ### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-648 | Исправлена логика передачи параметров, приводящая к ошибке при передаче параметра `name` без параметра `id`. |
| BANS-418 | Исправлена логика обновления сетей у группы базовой службы, из-за которой не происходило обновление внешних сетей. |
| BANS-646 | Исправлена логика повторного удаления группы базовой службы, которая приводила к возникновению ошибок. |
| BANS-559 | При создании группы с указанием параметра `timeoutStart` модуль производил попытку запустить группу после её создания. |
### Модуль decort_osimage
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-653 | Исправлена ошибка, приводящая к отображению неактуальной информации об образе после его удаления. |
### Модуль decort_vins
| Идентификатор<br>задачи | Описание | | Идентификатор<br>задачи | Описание |
| --- | --- | | --- | --- |
| BANS-817 | Модуль некорректно отслеживал завершение удаления и восстановления аккаунта. | | BANS-663 | Исправлена ошибка передачи параметра `description` при создании внутренней сети. |
| BANS-174 | Исправлена логика, приводящая к невозможности задать внешнюю сеть для внутренней сети без внешней сети. |

@ -5,6 +5,7 @@
| Версия платформы | Версия модулей Ansible | | Версия платформы | Версия модулей Ansible |
|:----------------:|:--------------------------:| |:----------------:|:--------------------------:|
| 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 |

@ -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,7 @@ 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'])
return return
def action(self,d_state): def action(self,d_state):
@ -176,7 +176,6 @@ class decort_bservice(DecortController):
), ),
state=dict( state=dict(
type='str', type='str',
default='present',
choices=[ choices=[
'absent', 'absent',
'disabled', 'disabled',
@ -298,25 +297,30 @@ def main():
subj.action(amodule.params['state']) subj.action(amodule.params['state'])
if amodule.params['state'] == 'absent': if amodule.params['state'] == 'absent':
subj.nop() subj.nop()
elif subj.bservice_info['status'] in ('ENABLED', 'DISABLED'): elif subj.bservice_info['status'] in (
'ENABLED', 'DISABLED', 'CREATED',
):
if amodule.params['state'] == 'absent': if amodule.params['state'] == 'absent':
subj.destroy() subj.destroy()
else: else:
subj.action(amodule.params['state']) subj.action(amodule.params['state'])
elif subj.bservice_info['status'] == "DESTROED": elif subj.bservice_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'): if amodule.params['state'] in ('present','enabled'):
subj.create() subj.create()
subj.action(amodule.params['state']) subj.action(amodule.params['state'])
if amodule.params['state'] == 'absent': if amodule.params['state'] == 'absent':
subj.nop() subj.nop()
else: else:
if amodule.params['state'] == 'absent': state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
subj.nop() subj.nop()
if amodule.params['state'] in ('present','started'): if state in ('present','started'):
subj.create() subj.create()
elif amodule.params['state'] in ('stopped', 'disabled','enabled'): elif state in ('stopped', 'disabled','enabled'):
subj.error() subj.error()
if subj.result['failed']: if subj.result['failed']:
amodule.fail_json(**subj.result) amodule.fail_json(**subj.result)
else: else:

@ -23,7 +23,7 @@ class decort_disk(DecortController):
self.disk_id = 0 self.disk_id = 0
self.account_id = 0 self.account_id = 0
# limitIO check for exclusive parameters # limitIO check for exclusive parameters
if arg_amodule.params['limitIO']: if arg_amodule.params['limitIO']:
self.disk_check_iotune_arg(arg_amodule.params['limitIO']) self.disk_check_iotune_arg(arg_amodule.params['limitIO'])
@ -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,15 +67,21 @@ 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
if self.disk_id:
self.acc_id = validated_disk_facts['accountId']
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
def create(self): def create(self):
self.disk_id = self.disk_create(
self.disk_id = self.disk_create(accountId=self.acc_id, accountId=self.acc_id,
name = self.amodule.params['name'], name = self.amodule.params['name'],
description=self.amodule.params['description'], description=self.amodule.params['description'],
size=self.amodule.params['size'], size=self.amodule.params['size'],
iops=self.amodule.params['iops'], sep_id=self.amodule.params['sep_id'],
sep_id=self.amodule.params['sep_id'], pool=self.amodule.params['pool'],
pool=self.amodule.params['pool'], storage_policy_id=self.aparams['storage_policy_id'],
) )
#IO tune #IO tune
if self.amodule.params['limitIO']: if self.amodule.params['limitIO']:
@ -115,16 +121,27 @@ class decort_disk(DecortController):
#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']) self.disk_share(self.disk_id,self.amodule.params['shareable'])
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.disk_change_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.disk_delete(disk_id=self.disk_id,
detach=self.amodule.params['force_detach'], detach=self.amodule.params['force_detach'],
permanently=self.amodule.params['permanently'], permanently=self.amodule.params['permanently'],
reason=self.amodule.params['reason']) reason=self.amodule.params['reason'])
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):
@ -175,6 +192,8 @@ 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']
return ret_dict return ret_dict
@ -219,10 +238,6 @@ 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',
options=dict( options=dict(
@ -287,6 +302,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,6 +312,73 @@ class decort_disk(DecortController):
], ],
) )
def check_amodule_args_for_change(self):
check_errors = False
if self.check_aparam_storage_policy_id() is False:
check_errors = True
if self.check_aparam_size() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
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 disk'
)
if self.check_aparam_storage_policy_id() is False:
check_errors = True
if self.check_aparam_size() is False:
check_errors = True
if check_errors:
self.exit(fail=True)
def check_aparam_storage_policy_id(self) -> bool:
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}'
)
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
def main(): def main():
decon = decort_disk() decon = decort_disk()
amodule = decon.amodule amodule = decon.amodule
@ -334,12 +419,12 @@ def main():
if amodule.params['state'] == 'absent': if amodule.params['state'] == 'absent':
decon.nop() decon.nop()
else: else:
decon.create() decon.create()
if decon.result['failed']: if decon.result['failed']:
amodule.fail_json(**decon.result) amodule.fail_json(**decon.result)
else: else:
if decon.result['changed'] and amodule.params['state'] in ('present'): if decon.result['changed']:
_, decon.disk_info = decon.disk_find(decon.disk_id) _, decon.disk_info = decon.disk_find(decon.disk_id)
decon.result['facts'] = decon.package_facts(amodule.check_mode) decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result) amodule.exit_json(**decon.result)

@ -36,11 +36,15 @@ 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):
"""No operation (NOP) handler for B-service. """No operation (NOP) handler for B-service.
@ -84,6 +88,17 @@ class decort_group(DecortController):
warning=True, warning=True,
) )
driver = self.aparams['driver']
if driver is None:
driver = 'KVM_X86'
self.message(
msg=self.MESSAGES.default_value_used(
param_name='driver',
default_value=driver,
),
warning=True,
)
self.group_id=self.group_provision( self.group_id=self.group_provision(
bs_id=self.bservice_id, bs_id=self.bservice_id,
arg_name=self.amodule.params['name'], arg_name=self.amodule.params['name'],
@ -92,14 +107,18 @@ 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'],
driver=driver,
) )
if self.amodule.params['state'] in ('started','present'): if (
self.amodule.params['state'] in ('started','present')
and self.amodule.params['timeoutStart'] is None
):
self.group_state(self.bservice_id,self.group_id,self.amodule.params['state']) self.group_state(self.bservice_id,self.group_id,self.amodule.params['state'])
self.group_should_exist = True self.group_should_exist = True
@ -196,6 +215,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 +249,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 +296,16 @@ class decort_group(DecortController):
'i440fx', 'i440fx',
] ]
), ),
storage_policy_id=dict(
type='int',
),
driver=dict(
type='str',
),
), ),
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 +314,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,9 +343,71 @@ 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']}'
)
aparam_driver = self.aparams['driver']
if (
aparam_driver is not None
and aparam_driver != self.group_info['driver']
):
check_errors = True
self.message(
msg='Check for parameter "driver" failed: '
'driver can not be changed'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create(self):
check_errors = False
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 group'
)
else:
if (
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 (
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: if check_errors:
self.exit(fail=True) self.exit(fail=True)
def main(): def main():
subj = decort_group() subj = decort_group()
amodule = subj.amodule amodule = subj.amodule
@ -359,8 +429,7 @@ def main():
if subj.group_id: if subj.group_id:
if subj.group_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING", if subj.group_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED", "ENABLING","DISABLING","RESTORING","MODELED","DESTROYED"):
"DISABLED","DESTROYED"):
subj.error() subj.error()
elif subj.group_info['status'] in ("DELETED","DESTROYED"): elif subj.group_info['status'] in ("DELETED","DESTROYED"):
if amodule.params['state'] == 'absent': if amodule.params['state'] == 'absent':

@ -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',
@ -448,6 +453,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 +507,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,6 +574,26 @@ 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)

@ -56,6 +56,7 @@ class decort_kvmvm(DecortController):
comp_id=self.aparams['clone_from']['id'], comp_id=self.aparams['clone_from']['id'],
) )
) )
self.rg_id = self.vm_to_clone_info['rgId']
if not self.vm_to_clone_id: if not self.vm_to_clone_id:
self.message( self.message(
f'Check for parameter "clone_from.id" failed: ' f'Check for parameter "clone_from.id" failed: '
@ -73,7 +74,7 @@ class decort_kvmvm(DecortController):
clone_id, clone_dict, _ = self.compute_find( clone_id, clone_dict, _ = self.compute_find(
comp_name=self.aparams['name'], comp_name=self.aparams['name'],
rg_id=self.vm_to_clone_info['rgId'], rg_id=self.rg_id,
) )
self.check_amodule_args_for_clone( self.check_amodule_args_for_clone(
clone_id=clone_id, clone_id=clone_id,
@ -389,7 +390,7 @@ class decort_kvmvm(DecortController):
Compute instance with the specified characteristics into the target Resource Group. Compute instance with the specified characteristics into the target Resource Group.
The target RG must exist. The target RG must exist.
""" """
# the following parameters must be present: cpu, ram, image_id or image_name # the following parameters must be present: cpu, ram, image_id
# each of the following calls will abort if argument is missing # each of the following calls will abort if argument is missing
self.check_amodule_argument('cpu') self.check_amodule_argument('cpu')
self.check_amodule_argument('ram') self.check_amodule_argument('ram')
@ -429,27 +430,15 @@ class decort_kvmvm(DecortController):
image_id, image_facts = None, None image_id, image_facts = None, None
if self.aparam_image: if self.aparam_image:
# either image_name or image_id must be present
if ( if (
self.check_amodule_argument('image_id', abort=False) self.check_amodule_argument('image_id', abort=False)
and self.amodule.params['image_id'] > 0 and self.amodule.params['image_id'] > 0
): ):
# find image by image ID and account ID # find image by image ID and account ID
# image_find(self, image_id, image_name, account_id, rg_id=0, sepid=0, pool=""): # image_find(self, image_id, account_id, rg_id=0, sepid=0, pool=""):
image_id, image_facts = self.image_find( image_id, image_facts = self.image_find(
image_id=self.amodule.params['image_id'], image_id=self.amodule.params['image_id'],
image_name="",
account_id=self.acc_id) account_id=self.acc_id)
elif (
self.check_amodule_argument('image_name', abort=False)
and self.amodule.params['image_name'] != ""
):
# find image by image name and account ID
image_id, image_facts = self.image_find(
image_id=0,
image_name=self.amodule.params['image_name'],
account_id=self.acc_id,
)
if validated_bdisk_size <= image_facts['size']: if validated_bdisk_size <= image_facts['size']:
# adjust disk size to the minimum allowed by OS image, which will be used to spin off this Compute # adjust disk size to the minimum allowed by OS image, which will be used to spin off this Compute
@ -523,26 +512,31 @@ class decort_kvmvm(DecortController):
# if we get through here, all parameters required to create new Compute instance should be at hand # if we get through here, all parameters required to create new Compute instance should be at hand
# NOTE: KVM VM is created in HALTED state and must be explicitly started # NOTE: KVM VM is created in HALTED state and must be explicitly started
self.comp_id = self.kvmvm_provision(rg_id=self.rg_id, self.comp_id = self.kvmvm_provision(
comp_name=self.amodule.params['name'], rg_id=self.rg_id,
cpu=self.amodule.params['cpu'], ram=self.amodule.params['ram'], comp_name=self.amodule.params['name'],
boot_disk_size=validated_bdisk_size, cpu=self.amodule.params['cpu'],
image_id=image_id, ram=self.amodule.params['ram'],
description=self.amodule.params['description'], boot_disk_size=validated_bdisk_size,
userdata=cloud_init_params, image_id=image_id,
sep_id=self.amodule.params['sep_id' ] if "sep_id" in self.amodule.params else None, description=self.amodule.params['description'],
pool_name=self.amodule.params['pool'] if "pool" in self.amodule.params else None, userdata=cloud_init_params,
start_on_create=start_compute, sep_id=self.amodule.params['sep_id' ] if "sep_id" in self.amodule.params else None,
chipset=chipset, pool_name=self.amodule.params['pool'] if "pool" in self.amodule.params else None,
cpu_pin=cpu_pin, start_on_create=start_compute,
hp_backed=hp_backed, chipset=chipset,
numa_affinity=numa_affinity, cpu_pin=cpu_pin,
preferred_cpu_cores=self.amodule.params['preferred_cpu_cores'], hp_backed=hp_backed,
boot_mode=boot_mode, numa_affinity=numa_affinity,
boot_loader_type=loader_type, preferred_cpu_cores=self.amodule.params['preferred_cpu_cores'],
network_interface_naming=network_interface_naming, boot_mode=boot_mode,
hot_resize=hot_resize, boot_loader_type=loader_type,
zone_id=self.aparams['zone_id'],) network_interface_naming=network_interface_naming,
hot_resize=hot_resize,
zone_id=self.aparams['zone_id'],
storage_policy_id=self.aparams['storage_policy_id'],
os_version=self.aparams['os_version'],
)
self.comp_should_exist = True self.comp_should_exist = True
# Originally we would have had to re-read comp_info after VM was provisioned # Originally we would have had to re-read comp_info after VM was provisioned
@ -579,7 +573,7 @@ class decort_kvmvm(DecortController):
if self.amodule.params['disks'] is not None: if self.amodule.params['disks'] is not None:
self.compute_disks( self.compute_disks(
comp_dict=self.comp_info, comp_dict=self.comp_info,
aparam_disks=self.amodule.params['disks'], aparam_disks_dict=self.amodule.params['disks'],
) )
self.compute_affinity(self.comp_info, self.compute_affinity(self.comp_info,
@ -626,8 +620,7 @@ class decort_kvmvm(DecortController):
Note that this handler deletes the VM permanently together with all assigned disk resources. Note that this handler deletes the VM permanently together with all assigned disk resources.
""" """
self.compute_delete(comp_id=self.comp_id, permanently=True) self.compute_delete(comp_id=self.comp_id, permanently=True)
self.comp_info['status'] = 'DESTROYED' self.comp_id, self.comp_info, _ = self._compute_get_by_id(self.comp_id)
self.comp_should_exist = False
return return
def restore(self): def restore(self):
@ -676,7 +669,7 @@ class decort_kvmvm(DecortController):
if self.amodule.params['disks'] is not None: if self.amodule.params['disks'] is not None:
self.compute_disks( self.compute_disks(
comp_dict=self.comp_info, comp_dict=self.comp_info,
aparam_disks=self.amodule.params['disks'], aparam_disks_dict=self.amodule.params['disks'],
) )
aparam_boot = self.amodule.params['boot'] aparam_boot = self.amodule.params['boot']
@ -695,6 +688,59 @@ class decort_kvmvm(DecortController):
if boot_disk_new_size: if boot_disk_new_size:
self.compute_bootdisk_size(self.comp_info, boot_disk_new_size) self.compute_bootdisk_size(self.comp_info, boot_disk_new_size)
boot_order = aparam_boot['order']
if (
boot_order is not None
and self.comp_info['bootOrder'] != boot_order
):
self.compute_set_boot_order(
vm_id=self.comp_id,
order=boot_order,
)
disk_redeploy = aparam_boot['disk_redeploy']
if disk_redeploy:
auto_start = False
if self.aparams['state'] is None:
if self.comp_info['techStatus'] == 'STARTED':
auto_start = True
else:
if self.aparams['state'] == 'started':
auto_start = True
disk_size = None
if (
aparam_boot is not None
and aparam_boot['disk_size'] is not None
):
disk_size = aparam_boot['disk_size']
elif self.aparams['image_id'] is not None:
_, image_facts = self.image_find(
image_id=self.aparams['image_id'],
)
disk_size = image_facts['size']
os_version = None
if (
self.aparams['image_id'] is None
or self.aparams['image_id'] == self.comp_info['imageId']
):
if self.aparams['os_version'] is None:
os_version = self.comp_info['os_version']
else:
os_version = self.aparams['os_version']
elif self.aparams['image_id'] != self.comp_info['imageId']:
os_version = self.aparams['os_version']
self.compute_disk_redeploy(
vm_id=self.comp_id,
storage_policy_id=self.aparams['storage_policy_id'],
image_id=self.aparams['image_id'],
disk_size=disk_size,
auto_start=auto_start,
os_version=os_version,
)
self.compute_resize(self.comp_info, self.compute_resize(self.comp_info,
self.amodule.params['cpu'], self.amodule.params['ram'], self.amodule.params['cpu'], self.amodule.params['ram'],
wait_for_state_change=arg_wait_cycles) wait_for_state_change=arg_wait_cycles)
@ -754,6 +800,28 @@ class decort_kvmvm(DecortController):
) )
) )
aparam_cdrom = self.aparams['cdrom']
if aparam_cdrom is not None:
mode = aparam_cdrom['mode']
image_id = aparam_cdrom['image_id']
if (
mode == 'insert'
and self.comp_info['cdImageId'] != image_id
):
self.compute_cd_insert(
vm_id=self.comp_id,
image_id=image_id,
)
elif mode == 'eject':
self.compute_cd_eject(
vm_id=self.comp_id,
)
if self.aparams['abort_cloning']:
self.compute_clone_abort(
vm_id=self.comp_id,
)
return return
@property @property
@ -773,6 +841,7 @@ class decort_kvmvm(DecortController):
'boot.loader_type': 'loaderType', 'boot.loader_type': 'loaderType',
'network_interface_naming': 'networkInterfaceNaming', 'network_interface_naming': 'networkInterfaceNaming',
'hot_resize': 'hotResize', 'hot_resize': 'hotResize',
'os_version': 'os_version',
} }
def get_nested_value( def get_nested_value(
@ -814,6 +883,14 @@ class decort_kvmvm(DecortController):
) )
if aparam_value is not None and aparam_value != comp_value: if aparam_value is not None and aparam_value != comp_value:
# If disk_redeploy = True no need to update os_version.
# Updating os_version through compute_disk_redeploy
if (
aparam_name == 'os_version'
and self.aparams['boot'] is not None
and self.aparams['boot']['disk_redeploy']
):
continue
result_args[aparam_name.replace('.', '_')] = ( result_args[aparam_name.replace('.', '_')] = (
aparam_value aparam_value
) )
@ -837,7 +914,6 @@ class decort_kvmvm(DecortController):
cpu="", cpu="",
ram="", ram="",
disk_size=0, disk_size=0,
data_disks=[], # IDs of attached data disks; this list can be emty
state="CHECK_MODE", state="CHECK_MODE",
tech_status="", tech_status="",
account_id=0, account_id=0,
@ -898,18 +974,19 @@ class decort_kvmvm(DecortController):
elif iface['connType'] == "VLAN": # This is direct external network connection elif iface['connType'] == "VLAN": # This is direct external network connection
ret_dict['public_ips'].append(iface['ipAddress']) ret_dict['public_ips'].append(iface['ipAddress'])
iface['security_group_mode'] = iface.pop('enable_secgroups')
iface['security_group_ids'] = iface.pop('security_groups')
ret_dict['cpu'] = self.comp_info['cpus'] ret_dict['cpu'] = self.comp_info['cpus']
ret_dict['ram'] = self.comp_info['ram'] ret_dict['ram'] = self.comp_info['ram']
ret_dict['image_id'] = self.comp_info['imageId'] ret_dict['image_id'] = self.comp_info['imageId']
for ddisk in self.comp_info['disks']: ret_dict['disks'] = self.comp_info['disks']
if ddisk['type'] == 'B': for disk in ret_dict['disks']:
if disk['type'] == 'B':
# if it is a boot disk - store its size # if it is a boot disk - store its size
ret_dict['disk_size'] = ddisk['sizeMax'] ret_dict['disk_size'] = disk['sizeMax']
elif ddisk['type'] == 'D':
# if it is a data disk - append its ID to the list of data disks IDs
ret_dict['data_disks'].append(ddisk['id'])
ret_dict['chipset'] = self.comp_info['chipset'] ret_dict['chipset'] = self.comp_info['chipset']
@ -960,15 +1037,30 @@ class decort_kvmvm(DecortController):
self.comp_info['snapshot_merge_status'] self.comp_info['snapshot_merge_status']
) )
ret_dict['cd_image_id'] = self.comp_info['cdImageId']
ret_dict['boot_order'] = self.comp_info['bootOrder']
ret_dict['os_version'] = self.comp_info['os_version']
ret_dict['boot_loader_metaiso'] = self.comp_info['loaderMetaIso']
if self.comp_info['loaderMetaIso'] is not None:
ret_dict['boot_loader_metaiso'] = {
'device_name': self.comp_info['loaderMetaIso']['devicename'],
'path': self.comp_info['loaderMetaIso']['path'],
}
if self.amodule.params['get_cloning_status']:
ret_dict['cloning_status'] = self.compute_get_clone_status(
vm_id=self.comp_id,
)
return ret_dict return ret_dict
def check_amodule_args_for_create(self): def check_amodule_args_for_create(self):
check_errors = False check_errors = False
# Check for unacceptable parameters for a blank Compute # Check for unacceptable parameters for a blank Compute
if ( if self.aparams['image_id'] is not None:
self.aparams['image_id'] is not None
or self.aparams['image_name'] is not None
):
self.aparam_image = True self.aparam_image = True
for param in ( for param in (
'network_interface_naming', 'network_interface_naming',
@ -1017,7 +1109,7 @@ class decort_kvmvm(DecortController):
check_errors = True check_errors = True
self.message( self.message(
f'Check for parameter "{parameter}" failed: ' f'Check for parameter "{parameter}" failed: '
f'"image_id" or "image_name" must be specified ' f'"image_id" must be specified '
f'to set {parameter}.' f'to set {parameter}.'
) )
@ -1029,7 +1121,7 @@ class decort_kvmvm(DecortController):
check_errors = True check_errors = True
self.message( self.message(
'Check for parameter "sep_id" failed: ' 'Check for parameter "sep_id" failed: '
'"image_id" or "image_name" or "boot.disk_size" ' '"image_id" or "boot.disk_size" '
'must be specified to set sep_id.' 'must be specified to set sep_id.'
) )
@ -1072,6 +1164,39 @@ class decort_kvmvm(DecortController):
if self.check_aparam_networks_trunk() is False: if self.check_aparam_networks_trunk() is False:
check_errors = True check_errors = True
if self.aparams['cdrom'] is not None:
check_errors = True
self.message(
'Check for parameter "cdrom" failed: '
'cdrom can be specified only for existing compute.'
)
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 compute'
)
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 self.aparams['abort_cloning'] is not None:
check_errors = True
self.message(
'Check for parameter "abort_cloning" failed: '
'abort_cloning can be specified only for existing compute.'
)
if check_errors: if check_errors:
self.exit(fail=True) self.exit(fail=True)
@ -1114,6 +1239,19 @@ class decort_kvmvm(DecortController):
'unknown', 'unknown',
], ],
), ),
from_cdrom=dict(
type='int',
),
order=dict(
type='list',
elements='str',
choices=[
e.value for e in self.VMBootDevice
],
),
disk_redeploy=dict(
type='bool',
),
), ),
), ),
sep_id=dict( sep_id=dict(
@ -1142,9 +1280,24 @@ class decort_kvmvm(DecortController):
], ],
default='update', default='update',
), ),
ids=dict( objects=dict(
type='list', type='list',
elements='int', elements='dict',
options=dict(
id=dict(
type='int',
required=True,
),
pci_slot_num_hex=dict(
type='str',
),
bus_num_hex=dict(
type='str',
),
),
required_together=[
('pci_slot_num_hex', 'bus_num_hex'),
],
), ),
), ),
), ),
@ -1155,9 +1308,6 @@ class decort_kvmvm(DecortController):
image_id=dict( image_id=dict(
type='int', type='int',
), ),
image_name=dict(
type='str',
),
name=dict( name=dict(
type='str', type='str',
), ),
@ -1190,6 +1340,16 @@ class decort_kvmvm(DecortController):
mac=dict( mac=dict(
type='str', type='str',
), ),
security_group_ids=dict(
type='list',
elements='int',
),
security_group_mode=dict(
type='bool',
),
enabled=dict(
type='bool',
),
), ),
required_if=[ required_if=[
('type', 'VINS', ('id',)), ('type', 'VINS', ('id',)),
@ -1197,7 +1357,7 @@ class decort_kvmvm(DecortController):
('type', 'VFNIC', ('id',)), ('type', 'VFNIC', ('id',)),
('type', 'DPDK', ('id',)), ('type', 'DPDK', ('id',)),
('type', 'TRUNK', ('id',)), ('type', 'TRUNK', ('id',)),
('type', 'SDN', ('id', 'mac')), ('type', 'SDN', ('id',)),
], ],
), ),
network_order_changing=dict( network_order_changing=dict(
@ -1326,6 +1486,16 @@ class decort_kvmvm(DecortController):
('name', 'timestamp', 'datetime'), ('name', 'timestamp', 'datetime'),
], ],
), ),
sep_pool_name=dict(
type='str',
),
sep_id=dict(
type='int',
),
storage_policy_id=dict(
type='int',
requiered=True,
),
), ),
), ),
network_interface_naming=dict( network_interface_naming=dict(
@ -1368,6 +1538,34 @@ class decort_kvmvm(DecortController):
get_snapshot_merge_status=dict( get_snapshot_merge_status=dict(
type='bool', type='bool',
), ),
cdrom=dict(
type='dict',
options=dict(
mode=dict(
type='str',
choices=[
'insert',
'eject',
],
default='insert',
),
image_id=dict(
type='int',
),
),
),
storage_policy_id=dict(
type='int',
),
os_version=dict(
type='str',
),
get_cloning_status=dict(
type='bool',
),
abort_cloning=dict(
type='bool',
),
), ),
supports_check_mode=True, supports_check_mode=True,
required_one_of=[ required_one_of=[
@ -1377,7 +1575,7 @@ class decort_kvmvm(DecortController):
'clone_from': 'name', 'clone_from': 'name',
}, },
) )
def check_amodule_args_for_change(self): def check_amodule_args_for_change(self):
check_errors = False check_errors = False
@ -1404,30 +1602,6 @@ class decort_kvmvm(DecortController):
aparam_boot = self.amodule.params['boot'] aparam_boot = self.amodule.params['boot']
if aparam_boot is not None: if aparam_boot is not None:
new_boot_disk_size = aparam_boot['disk_size']
if new_boot_disk_size is not None:
boot_disk_size = 0
for disk in self.comp_info['disks']:
if disk['type'] == 'B':
boot_disk_size = disk['sizeMax']
break
else:
if aparam_boot is None or aparam_boot['disk_id'] is None:
check_errors = True
self.message(
f'Can\'t set boot disk size for Compute '
f'{comp_id}, because it doesn\'t '
f'have a boot disk.'
)
if new_boot_disk_size < boot_disk_size:
check_errors = True
self.message(
f'New boot disk size {new_boot_disk_size} is less'
f' than current {boot_disk_size} for Compute ID '
f'{comp_id}'
)
aparam_disks = self.amodule.params['disks'] aparam_disks = self.amodule.params['disks']
aparam_boot_disk_id = aparam_boot['disk_id'] aparam_boot_disk_id = aparam_boot['disk_id']
comp_disk_ids = [disk['id'] for disk in self.comp_info['disks']] comp_disk_ids = [disk['id'] for disk in self.comp_info['disks']]
@ -1479,6 +1653,75 @@ class decort_kvmvm(DecortController):
f'to Compute ID {self.comp_id}.' f'to Compute ID {self.comp_id}.'
) )
if self.check_aparam_boot_disk_redeploy() is False:
check_errors = True
new_boot_disk_size = aparam_boot['disk_size']
if new_boot_disk_size is not None:
boot_disk_size = 0
for disk in self.comp_info['disks']:
if disk['type'] == 'B':
boot_disk_size = disk['sizeMax']
break
else:
if aparam_boot is None or aparam_boot['disk_id'] is None:
check_errors = True
self.message(
f'Can\'t set boot disk size for Compute '
f'{comp_id}, because it doesn\'t '
f'have a boot disk.'
)
if new_boot_disk_size < boot_disk_size:
check_errors = True
self.message(
f'New boot disk size {new_boot_disk_size} is less'
f' than current {boot_disk_size} for Compute ID '
f'{comp_id}'
)
cd_rom_image_id = aparam_boot['from_cdrom']
if cd_rom_image_id is not None:
if not (
self.comp_info['techStatus'] == 'STOPPED'
and self.aparams['state'] == 'started'
):
check_errors = True
self.message(
f'Check for parameter "boot.from_cdrom" failed: '
f'VM ID {self.comp_id} must be stopped and "state" '
'must be "started" to boot from CD-ROM.'
)
_, image_info = self._image_get_by_id(
image_id=cd_rom_image_id,
)
if image_info is None:
check_errors = True
self.message(
'Check for parameter "boot.from_cdrom" failed: '
f'Image ID {cd_rom_image_id} not found.'
)
elif image_info['type'] != 'cdrom':
check_errors = True
self.message(
'Check for parameter "boot.from_cdrom" failed: '
f'Image ID {cd_rom_image_id} is not a cd-rom type.'
)
boot_order_list = aparam_boot['order']
if boot_order_list is not None:
boot_order_duplicates = set([
boot_dev for boot_dev in boot_order_list
if boot_order_list.count(boot_dev) > 1
])
if boot_order_duplicates:
check_errors = True
self.message(
'Check for parameter "boot.order" failed: '
'List of boot devices has duplicates: '
f'{boot_order_duplicates}.'
)
if ( if (
not comp_info['imageId'] not comp_info['imageId']
and self.amodule.params['state'] in ( and self.amodule.params['state'] in (
@ -1574,24 +1817,24 @@ class decort_kvmvm(DecortController):
'VM must be started to get console url.' 'VM must be started to get console url.'
) )
aparam_disks = self.aparams['disks'] aparam_disks_dict = self.aparams['disks']
if aparam_disks is not None: if aparam_disks_dict is not None:
aparam_disks_ids = aparam_disks['ids'] aparam_disks = aparam_disks_dict.get('objects', [])
aparam_disks_ids = [disk['id'] for disk in aparam_disks]
comp_boot_disk_id = None comp_boot_disk_id = None
for comp_disk in self.comp_info['disks']: for comp_disk in self.comp_info['disks']:
if comp_disk['type'] == 'B': if comp_disk['type'] == 'B':
comp_boot_disk_id = comp_disk['id'] comp_boot_disk_id = comp_disk['id']
break break
disks_to_detach = [] disks_to_detach = []
match aparam_disks['mode']: match aparam_disks_dict['mode']:
case 'detach' | 'delete': case 'detach' | 'delete':
disks_to_detach = aparam_disks_ids disks_to_detach = aparam_disks_ids
case 'match': case 'match':
comp_disk_ids = { comp_disk_ids = {
disk['id'] for disk in self.comp_info['disks'] disk['id'] for disk in self.comp_info['disks']
} }
disks = set(aparam_disks_ids) disks_to_detach = comp_disk_ids - set(aparam_disks_ids)
disks_to_detach = comp_disk_ids - disks
if ( if (
comp_boot_disk_id is not None comp_boot_disk_id is not None
and comp_boot_disk_id in disks_to_detach and comp_boot_disk_id in disks_to_detach
@ -1611,6 +1854,18 @@ class decort_kvmvm(DecortController):
f'Compute ID {self.comp_id} while snapshots exist.' f'Compute ID {self.comp_id} while snapshots exist.'
) )
if aparam_disks_dict['mode'] in ('delete', 'detach'):
for disk in aparam_disks:
for param, value in disk.items():
if param != 'id' and value is not None:
check_errors = True
self.message(
msg='Check for parameter "disks.objects" '
'failed: only disk id can be specified if '
'disks.mode is "delete" or "detach"'
)
break
if ( if (
( (
self.aparams['cpu'] is not None self.aparams['cpu'] is not None
@ -1637,11 +1892,72 @@ class decort_kvmvm(DecortController):
aparam_networks = self.aparams['networks'] aparam_networks = self.aparams['networks']
if aparam_networks is not None: if aparam_networks is not None:
vm_networks = self.comp_info['interfaces']
if (
not vm_networks
and not self.is_vm_stopped_or_will_be_stopped
):
check_errors = True
self.message(
'Check for parameter "networks" failed: '
'VM must be stopped before attach it\'s first network.'
)
vm_networks_ids = [
network['netId'] for network in vm_networks
if network['type'] != self.VMNetType.EMPTY.value
]
aparam_networks_ids = [
network['id'] for network in aparam_networks
if network['type'] != self.VMNetType.EMPTY.value
]
new_networks = list(
set(aparam_networks_ids) - set(vm_networks_ids)
)
net_types = {net['type'] for net in aparam_networks} net_types = {net['type'] for net in aparam_networks}
if new_networks:
if not (
len(new_networks) == 1
and self.VMNetType.DPDK.value in net_types
) and not self.is_vm_stopped_or_will_be_stopped:
check_errors = True
self.message(
'Check for parameter "networks" failed: '
'VM must be stopped to attach non-DPDK network.'
)
if self.VMNetType.TRUNK.value in net_types: if self.VMNetType.TRUNK.value in net_types:
if self.check_aparam_networks_trunk() is False: if self.check_aparam_networks_trunk() is False:
check_errors = True check_errors = True
for network in aparam_networks:
if (
network['enabled'] is not None
and network['type'] not in [
self.VMNetType.VINS.value,
self.VMNetType.EXTNET.value,
self.VMNetType.DPDK.value,
self.VMNetType.SDN.value,
self.VMNetType.TRUNK.value,
]
):
check_errors = True
self.message(
'Check for parameter "networks.enabled" failed: '
'Can not enable or disable network '
f'ID {network['id']} and type {network['type']}.'
'Only networks of type VINS, EXTNET, DPDK, SDN, TRUNK '
'can be enabled or disabled.'
)
if self.check_aparam_cdrom() is False:
check_errors = True
if self.check_aparam_storage_policy_id() is False:
check_errors = True
if self.check_aparam_image_id() is False:
check_errors = True
if check_errors: if check_errors:
self.exit(fail=True) self.exit(fail=True)
@ -1731,6 +2047,9 @@ class decort_kvmvm(DecortController):
snapshot_timestamp=snapshot_timestamp, snapshot_timestamp=snapshot_timestamp,
snapshot_name=snapshot_name, snapshot_name=snapshot_name,
snapshot_datetime=snapshot_datetime, snapshot_datetime=snapshot_datetime,
sep_pool_name=self.aparams['clone_from']['sep_pool_name'],
sep_id=self.aparams['clone_from']['sep_id'],
storage_policy_id=self.aparams['clone_from']['storage_policy_id'],
) )
return clone_id return clone_id
@ -1812,6 +2131,130 @@ class decort_kvmvm(DecortController):
return not check_errors return not check_errors
def check_aparam_cdrom(self) -> bool | None:
check_errors = False
aparam_cdrom = self.aparams['cdrom']
if aparam_cdrom is not None:
mode = aparam_cdrom['mode']
if self.is_vm_stopped_or_will_be_stopped:
check_errors = True
self.message(
'Check for parameter "cdrom" failed: '
f'VM ID {self.comp_id} must be started to {mode} '
f'CD-ROM.'
)
image_id = aparam_cdrom['image_id']
match mode:
case 'insert':
if image_id is None:
check_errors = True
self.message(
'Check for parameter "cdrom.image_id" failed: '
f'cdrom.image_id must be specified '
f'if cdrom.mode is "insert".'
)
_, image_info = self._image_get_by_id(
image_id=image_id,
)
if image_info is None:
check_errors = True
self.message(
'Check for parameter "cdrom.image_id" failed: '
f'Image ID {image_id} not found.'
)
elif image_info['type'] != 'cdrom':
check_errors = True
self.message(
'Check for parameter "cdrom.image_id" failed: '
f'Image ID {image_id} is not a CD-ROM type.'
)
case 'eject':
if image_id is not None:
check_errors = True
self.message(
'Check for parameter "cdrom.image_id" failed: '
f'cdrom.image_id must not be specified '
f'if cdrom.mode is "eject".'
)
if not self.comp_info['cdImageId']:
check_errors = True
self.message(
'Check for parameter "cdrom.mode" failed: '
f'VM ID {self.comp_id} does not have CD-ROM '
'to eject.'
)
return not check_errors
def check_aparam_storage_policy_id(self) -> bool:
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is not None:
for disk in self.comp_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 for compute '
f'ID {self.comp_id} disk ID {disk['id']}'
)
return not check_errors
def check_aparam_boot_disk_redeploy(self) -> bool:
check_errors = False
disk_redeploy = self.aparams['boot']['disk_redeploy']
if disk_redeploy:
if self.aparams['storage_policy_id'] is None:
check_errors = True
self.message(
'Check for parameter "storage_policy_id" failed:'
'"storage_policy_id" must be specified to redeploy.'
)
vm_has_boot_disk = False
for disk in self.comp_info['disks']:
if disk['type'] == 'B':
vm_has_boot_disk = True
break
if not vm_has_boot_disk:
check_errors = True
self.message(
'Check for parameter "boot.redeploy" failed: '
'VM does not have boot disk to redeploy.'
)
aparam_disks = self.amodule.params['disks']
if aparam_disks is not None and aparam_disks['mode'] == 'match':
check_errors = True
self.message(
'Check for parameter "disks.mode" failed: '
'"disks.mode" must not be "match" to redeploy.'
)
return not check_errors
def check_aparam_image_id(self) -> bool:
check_errors = False
aparam_image_id = self.aparams['image_id']
if aparam_image_id is not None:
if aparam_image_id != self.comp_info['imageId']:
if (
self.aparams['boot'] is None
or self.aparams['boot']['disk_redeploy'] is None
):
check_errors = True
self.message(
'Check for parameter "image_id" failed: '
'"boot.disk_redeploy" must be set to True to change '
'VM image.'
)
return not check_errors
def find_networks_tags_intersections( def find_networks_tags_intersections(
self, self,
trunk_networks: list, trunk_networks: list,
@ -1958,7 +2401,7 @@ def main():
amodule = subj.amodule amodule = subj.amodule
if subj.comp_id: if subj.comp_id:
if subj.comp_info['status'] in ("DISABLED", "MIGRATING", "DELETING", "DESTROYING", "ERROR", "REDEPLOYING"): if subj.comp_info['status'] in ("MIGRATING", "DELETING", "DESTROYING", "ERROR", "REDEPLOYING"):
# cannot do anything on the existing Compute in the listed states # cannot do anything on the existing Compute in the listed states
subj.error() # was subj.nop() subj.error() # was subj.nop()
elif subj.comp_info['status'] in ("ENABLED", "DISABLED"): elif subj.comp_info['status'] in ("ENABLED", "DISABLED"):

@ -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=[
@ -411,10 +407,10 @@ def main():
if decon.result['failed']: if decon.result['failed']:
amodule.fail_json(**decon.result) amodule.fail_json(**decon.result)
else: else:
if decon.result['changed'] and amodule.params['state'] != 'absent': if decon.result['changed']:
_, decon.lb_facts = decon.lb_find(decon.lb_id) _, decon.lb_facts = decon.lb_find(lb_id=decon.lb_id)
if decon.lb_id: decon.result['facts'] = decon.package_facts(amodule.check_mode)
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result) amodule.exit_json(**decon.result)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

@ -22,7 +22,8 @@ class decort_osimage(DecortController):
self.validated_virt_image_id = 0 self.validated_virt_image_id = 0
self.validated_image_name = amodule.params['image_name'] self.validated_image_name = amodule.params['image_name']
self.validated_virt_image_name = None self.validated_virt_image_name = None
self.validated_virt_image_id = amodule.params['virt_id'] self.image_info: dict
self.virt_image_info: dict
if amodule.params['account_name']: if amodule.params['account_name']:
self.validated_account_id, _ = self.account_find(amodule.params['account_name']) self.validated_account_id, _ = self.account_find(amodule.params['account_name'])
else: else:
@ -34,23 +35,51 @@ class decort_osimage(DecortController):
self.result['changed'] = False self.result['changed'] = False
self.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name']) self.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name'])
amodule.fail_json(**self.result) amodule.fail_json(**self.result)
self.acc_id = self.validated_account_id
if (
if amodule.params['virt_id'] != 0 and amodule.params['virt_name']: self.aparams['virt_id'] != 0
self.validated_virt_image_id, image_facts =\ or self.aparams['virt_name'] is not None
):
self.validated_virt_image_id, self.virt_image_info = (
self.decort_virt_image_find(amodule) self.decort_virt_image_find(amodule)
if (self.validated_virt_image_id and )
amodule.params['virt_name'] != image_facts['name']): if self.virt_image_info:
self.decort_virt_image_rename(amodule) _, linked_image_info = self._image_get_by_id(
self.result['msg'] = 'Virtual image renamed successfully' image_id=self.virt_image_info['linkTo']
elif amodule.params['image_id'] != 0 and amodule.params['image_name']: )
self.validated_image_id, image_facts = self.decort_image_find(amodule) self.acc_id = linked_image_info['accountId']
if (self.validated_image_id and if (
amodule.params['image_name'] != image_facts['name']): self.aparams['virt_name'] is not None
decort_osimage.decort_image_rename(self,amodule) and self.aparams['virt_name']
self.result['msg'] = ("Image renamed successfully") != 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_osimage.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): def decort_image_find(self, amodule):
# function that finds the OS image # function that finds the OS image
@ -121,21 +150,22 @@ class decort_osimage(DecortController):
) )
# function that creates OS image # function that creates OS image
image_facts = self.image_create(img_name=self.validated_image_name, image_facts = self.image_create(
url=amodule.params['url'], img_name=self.validated_image_name,
gid=amodule.params['gid'], url=amodule.params['url'],
boot_mode=boot_mode, boot_mode=boot_mode,
boot_loader_type=loader_type, boot_loader_type=loader_type,
hot_resize=hot_resize, hot_resize=hot_resize,
username=amodule.params['image_username'], username=amodule.params['image_username'],
password=amodule.params['image_password'], password=amodule.params['image_password'],
account_id=self.validated_account_id, account_id=self.validated_account_id,
usernameDL=amodule.params['usernameDL'], usernameDL=amodule.params['usernameDL'],
passwordDL=amodule.params['passwordDL'], passwordDL=amodule.params['passwordDL'],
sepId=amodule.params['sepId'], sepId=amodule.params['sepId'],
poolName=amodule.params['poolName'], poolName=amodule.params['poolName'],
drivers=amodule.params['drivers'], network_interface_naming=network_interface_naming,
network_interface_naming=network_interface_naming) storage_policy_id=amodule.params['storage_policy_id'],
)
self.result['changed'] = True self.result['changed'] = True
return image_facts return image_facts
@ -151,8 +181,9 @@ class decort_osimage(DecortController):
def decort_image_delete(self,amodule): def decort_image_delete(self,amodule):
# function that removes an image # function that removes an image
self.image_delete(imageId=amodule.image_id_delete) self.image_delete(imageId=amodule.image_id_delete)
self.result['changed'] = True _, image_facts = decort_osimage._image_get_by_id(self, amodule.image_id_delete)
self.result['msg'] = ("Image '{}' deleted").format(amodule.image_id_delete) self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
return
def decort_virt_image_create(self,amodule): def decort_virt_image_create(self,amodule):
# function that creates a virtual image # function that creates a virtual image
@ -231,6 +262,8 @@ class decort_osimage(DecortController):
'networkInterfaceNaming' 'networkInterfaceNaming'
] ]
ret_dict['hot_resize'] = arg_osimage_facts['hotResize'] ret_dict['hot_resize'] = arg_osimage_facts['hotResize']
ret_dict['storage_policy_id'] = arg_osimage_facts['storage_policy_id']
ret_dict['to_clean'] = arg_osimage_facts['to_clean']
return ret_dict return ret_dict
@property @property
@ -273,17 +306,9 @@ class decort_osimage(DecortController):
'present', 'present',
], ],
), ),
drivers=dict(
type='str',
default='KVM_X86',
),
url=dict( url=dict(
type='str', type='str',
), ),
gid=dict(
type='int',
default=0,
),
sepId=dict( sepId=dict(
type='int', type='int',
default=0, default=0,
@ -333,10 +358,102 @@ class decort_osimage(DecortController):
'eth', 'eth',
], ],
), ),
storage_policy_id=dict(
type='int',
),
), ),
supports_check_mode=True, 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)
def main(): def main():
decon = decort_osimage() decon = decort_osimage()
amodule = decon.amodule amodule = decon.amodule
@ -360,15 +477,31 @@ def main():
decon.result['msg'] = ("Cannot find OS image") decon.result['msg'] = ("Cannot find OS image")
amodule.fail_json(**decon.result) amodule.fail_json(**decon.result)
if decon.validated_virt_image_id and decon.target_image_id: if decon.validated_virt_image_id:
if decort_osimage.decort_osimage_package_facts(image_facts)['linkto'] != decon.target_image_id: if (
decon.target_image_id
and decort_osimage.decort_osimage_package_facts(image_facts)[
'linkto'
] != decon.target_image_id
):
decort_osimage.decort_virt_image_link(decon,amodule) decort_osimage.decort_virt_image_link(decon,amodule)
decon.result['changed'] = True decon.result['changed'] = True
amodule.exit_json(**decon.result) amodule.exit_json(**decon.result)
if (
amodule.params['storage_policy_id'] is not None
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
decon.image_change_storage_policy(
image_id=decon.validated_virt_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
if decon.validated_virt_image_id > 0 and amodule.params['state'] == "absent": if amodule.params['state'] == "absent" and decon.validated_virt_image_id:
amodule.image_id_delete = decon.validated_virt_image_id amodule.image_id_delete = decon.validated_virt_image_id
decort_osimage.decort_image_delete(decon, amodule) image_id, image_facts = decort_osimage.decort_virt_image_find(decon, amodule)
if image_facts['status'] != 'PURGED':
decort_osimage.decort_image_delete(decon,amodule)
elif amodule.params['image_name'] or amodule.params['image_id']: elif amodule.params['image_name'] or amodule.params['image_id']:
image_id, image_facts = decort_osimage.decort_image_find(decon, amodule) image_id, image_facts = decort_osimage.decort_image_find(decon, amodule)
@ -385,16 +518,38 @@ def main():
decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id'] decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
elif amodule.params['state'] == "absent" and decon.validated_image_id: elif amodule.params['state'] == "absent" and decon.validated_image_id:
amodule.image_id_delete = decon.validated_image_id amodule.image_id_delete = decon.validated_image_id
image_id, image_facts = decort_osimage.decort_image_find(decon, amodule)
if image_facts['status'] != 'DESTROYED':
decort_osimage.decort_image_delete(decon,amodule) decort_osimage.decort_image_delete(decon,amodule)
if decon.validated_image_id:
if (
amodule.params['storage_policy_id'] is not None
and amodule.params['storage_policy_id']
!= image_facts['storage_policy_id']
):
decon.image_change_storage_policy(
image_id=decon.validated_image_id,
storage_policy_id=amodule.params['storage_policy_id'],
)
if decon.result['failed'] == True: if decon.result['failed'] == True:
# we failed to find the specified image - fail the module # we failed to find the specified image - fail the module
decon.result['changed'] = False decon.result['changed'] = False
amodule.fail_json(**decon.result) amodule.fail_json(**decon.result)
else:
if decon.validated_image_id:
_, image_facts = decon.decort_image_find(amodule=amodule)
elif decon.validated_virt_image_id:
_, image_facts = decon.decort_virt_image_find(amodule=amodule)
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(
arg_osimage_facts=image_facts,
arg_check_mode=amodule.check_mode,
)
amodule.exit_json(**decon.result) amodule.exit_json(**decon.result)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

@ -97,26 +97,57 @@ class decort_rg(DecortController):
self.amodule.params['rg_name'], self.amodule.params['rg_name'],
self.validated_acc_id) self.validated_acc_id)
return return
def update(self): def update(self):
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',
net_transfer='exttraffic',
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)
self.result['failed'] = True self.result['failed'] = True
if not self.result['failed']: if not self.result['failed']:
self.rg_update( self.rg_update(
arg_rg_dict=self.rg_facts, arg_rg_dict=self.rg_facts,
@ -243,6 +274,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

@ -0,0 +1,351 @@
#!/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
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=[
e.name for e in
self.SecurityGroupRuleDirection
],
required=True,
),
ethertype=dict(
type='str',
choices=[
e.name for e in
self.SecurityGroupRuleEtherType
],
),
id=dict(
type='int',
),
port_range=dict(
type='dict',
options=dict(
min=dict(
type='int',
),
max=dict(
type='int',
),
),
),
protocol=dict(
type='str',
choices=[
e.name for e in
self.SecurityGroupRuleProtocol
],
),
remote_ip_prefix=dict(
type='str',
),
),
),
),
),
state=dict(
type='str',
choices=[e.value for e in self.SecurityGroupState],
),
),
supports_check_mode=True,
)
def run(self):
if self.aparams['id'] is not None:
self.id = self.aparams['id']
elif self.aparams['name'] is not None:
security_group = self.security_group_find(
account_id=self.aparams['account_id'],
name=self.aparams['name'],
)
if security_group:
self.id = security_group['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):
self.facts: dict = self.security_group_get(id=self.id)
self.facts['created_timestamp'] = self.facts.pop('created_at')
self.facts['updated_timestamp'] = self.facts.pop('updated_at')
for rule in self.facts['rules']:
rule['port_range'] = {
'min': rule.pop('port_range_min'),
'max': rule.pop('port_range_max'),
}
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):
security_groups_by_account_id = self.user_security_groups(
account_id=self.aparams['account_id']
)
sg_names = [sg['name'] for sg in security_groups_by_account_id]
if self.aparams['name'] not in sg_names:
self.id = self.security_group_create(
account_id=self.aparams['account_id'],
name=self.aparams['name'],
description=self.aparams['description'],
)
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.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.security_group_detele(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.security_group_create_rule(
security_group_id=self.id,
direction=self.SecurityGroupRuleDirection[rule['direction']],
ethertype=(
self.SecurityGroupRuleEtherType[rule['ethertype']]
if rule.get('ethertype') else None
),
protocol=(
self.SecurityGroupRuleProtocol[rule['protocol']]
if rule.get('protocol') else None
),
port_range_min=port_range_min,
port_range_max=port_range_max,
remote_ip_prefix=rule.get('remote_ip_prefix'),
)
def main():
DecortSecurityGroup().run()
if __name__ == '__main__':
main()

@ -0,0 +1,50 @@
#!/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
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,
)
def run(self):
self.get_info()
self.exit()
def get_info(self):
self.facts = self.storage_policy_get(id=self.id)
self.facts['sep_pools'] = self.facts.pop('access_seps_pools')
self.facts['iops_limit'] = self.facts.pop('limit_iops')
self.facts['usage']['account_ids'] = self.facts['usage'].pop(
'accounts'
)
self.facts['usage']['rg_ids'] = self.facts['usage'].pop('resgroups')
def main():
DecortStoragePolicy().run()
if __name__ == '__main__':
main()

@ -48,6 +48,9 @@ class DecortUserInfo(DecortController):
e.value for e in self.AccountStatus e.value for e in self.AccountStatus
], ],
), ),
zone_id=dict(
type='int',
),
), ),
), ),
pagination=dict( pagination=dict(
@ -303,6 +306,143 @@ class DecortUserInfo(DecortController):
), ),
), ),
), ),
storage_policies=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
account_id=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
iops_limit=dict(
type='int',
),
name=dict(
type='str',
),
pool_name=dict(
type='str',
),
rg_id=dict(
type='int',
),
sep_id=dict(
type='int',
),
status=dict(
type='str',
choices=[
e.value for e
in self.StoragePolicyStatus
],
),
),
),
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.StoragePoliciesSortableField
],
required=True,
),
),
),
),
),
security_groups=dict(
type='dict',
options=dict(
filter=dict(
type='dict',
options=dict(
account_id=dict(
type='int',
),
created_timestamp_max=dict(
type='int',
),
created_timestamp_min=dict(
type='int',
),
description=dict(
type='str',
),
id=dict(
type='int',
),
name=dict(
type='str',
),
updated_timestamp_max=dict(
type='int',
),
updated_timestamp_min=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=[
e.name for e
in self.SecurityGroupSortableField
],
required=True,
),
),
),
),
),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
@ -390,6 +530,8 @@ class DecortUserInfo(DecortController):
self.AccountStatus(input_args_filter_status) self.AccountStatus(input_args_filter_status)
) )
mapped_args['zone_id'] = input_args_filter['zone_id']
input_args_pagination = input_args['pagination'] input_args_pagination = input_args['pagination']
if input_args_pagination: if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number'] mapped_args['page_number'] = input_args_pagination['number']
@ -552,6 +694,82 @@ class DecortUserInfo(DecortController):
return mapped_args return mapped_args
@property
def mapped_storage_policies_args(self):
"""
Map the module argument `storage_policies` to
arguments dictionary for the method
`DecortController.user_storage_policies`.
"""
input_args = self.aparams['storage_policies']
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.StoragePolicyStatus(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.StoragePoliciesSortableField(input_args_sorting_field)
)
return mapped_args
@property
def mapped_security_groups_args(self):
"""
Map the module argument `security_groups` to
arguments dictionary for the method
`DecortController.user_security_groups`.
"""
input_args = self.aparams['security_groups']
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_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.SecurityGroupSortableField[input_args_sorting_field]
)
return mapped_args
def run(self): def run(self):
self.get_info() self.get_info()
self.exit() self.exit()
@ -606,6 +824,52 @@ class DecortUserInfo(DecortController):
trunk_facts['ovs_bridge'] = trunk_facts.pop('ovsBridge') trunk_facts['ovs_bridge'] = trunk_facts.pop('ovsBridge')
trunk_facts['vlan_ids'] = trunk_facts.pop('trunkTags') trunk_facts['vlan_ids'] = trunk_facts.pop('trunkTags')
if self.aparams['storage_policies']:
self.facts['storage_policies'] = self.user_storage_policies(
**self.mapped_storage_policies_args
)
for storage_policy_facts in self.facts['storage_policies']:
storage_policy_facts['sep_pools'] = storage_policy_facts.pop(
'access_seps_pools'
)
storage_policy_facts['iops_limit'] = storage_policy_facts.pop(
'limit_iops'
)
storage_policy_facts['usage']['account_ids'] = (
storage_policy_facts['usage'].pop('accounts')
)
storage_policy_facts['usage']['rg_ids'] = (
storage_policy_facts['usage'].pop('resgroups')
)
if self.aparams['security_groups']:
self.facts['security_groups'] = self.user_security_groups(
**self.mapped_security_groups_args
)
for security_groups_facts in self.facts['security_groups']:
for rule in security_groups_facts.get('rules', []):
rule['port_range'] = {
'min': rule.pop('port_range_min'),
'max': rule.pop('port_range_max'),
}
security_groups_facts['created_timestamp'] = (
security_groups_facts.pop('created_at')
)
security_groups_facts['created_timestamp_readable'] = (
self.sec_to_dt_str(security_groups_facts[
'created_timestamp'
])
)
security_groups_facts['updated_timestamp'] = (
security_groups_facts.pop('updated_at')
)
security_groups_facts['updated_timestamp_readable'] = (
self.sec_to_dt_str(security_groups_facts[
'updated_timestamp'
])
)
def main(): def main():
DecortUserInfo().run() DecortUserInfo().run()

@ -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,
@ -416,8 +420,9 @@ def main():
vins_should_exist = True vins_should_exist = True
elif amodule.params['state'] == 'absent': elif amodule.params['state'] == 'absent':
# destroy permanently # destroy permanently
decon.delete() if decon.amodule.params['permanently']:
vins_should_exist = False decon.delete()
vins_should_exist = False
elif amodule.params['state'] == 'disabled': elif amodule.params['state'] == 'disabled':
decon.error() decon.error()
vins_should_exist = False vins_should_exist = False

@ -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()
@ -181,7 +181,7 @@ class DecortSnapshot(DecortController):
def main(): def main():
DecortSnapshot().run() DecortVMSnapshot().run()
if __name__ == '__main__': if __name__ == '__main__':

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save