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>задачи | Описание |
| --- | --- |
| BANS-798 | Обновлены системные требования: версия интерпретатора Python обновлена до 3.12, версия Python-библиотеки ansible обновлена до 11.6.0 |
| BANS-807 | Модуль переименован из `decort_snapshot` в `decort_vm_snapshot`. |
### Модуль decort_kvmvm
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-790 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. |
| BANS-810 | Добавлен параметр `guest_agent` и возвращаемое значение `guest_agent`. |
| BANS-806 | Добавлен параметр `get_snapshot_merge_status` и возвращаемое значение `snapshot_merge_status`. |
| BANS-823 | Добавлено значение `TRUNK` для параметра `networks.type`. |
| BANS-813 | Добавлено значение `SDN` для параметра `networks.type`. |
| BANS-835 | Добавлена возможность использования параметра `networks.mtu` для внешней сети. |
| BANS-723 | Добавлен параметр `cdrom` и возвращаемое значение `cd_image_id`. |
| BANS-727 | Добавлен параметр `boot.from_cdrom`. |
| BANS-728 | Добавлен параметр `boot.order` и возвращаемое значение `boot_order`. |
| BANS-871 | Добавлен параметр `storage_policy_id`. |
| BANS-729 | Добавлен параметр `boot.disk_redeploy`. |
| 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>задачи | Описание |
| --- | --- |
| BANS-793 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. |
| BANS-819 | Добавлено возвращаемое значение `account_id`. |
| BANS-800 | Добавлены значения `stopped` и `started` для параметра `state` и возвращаемое значение `tech_status`. |
| BANS-865 | Добавлен параметр `storage_policy_id`. |
### Модуль decort_k8s
### Модуль decort_disk
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-794 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. |
| BANS-804 | Добавлены значения `stopped` и `started` для параметра `state`. |
| BANS-867 | Добавлен параметр `storage_policy_id` и возвращаемое значение `storage_policy_id`. |
| 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>задачи | Описание |
| --- | --- |
| 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>задачи | Описание |
| --- | --- |
| BANS-792 | Добавлен параметр `zone_id` и возвращаемое значение `zone_id`. |
| BANS-805 | Добавлены значения `stopped` и `started` для параметра `state`. |
| BANS-869 | Добавлен параметр `storage_policy_id`. |
| BANS-234 | Добавлена возможность переименования кластера. |
### Модуль decort_user_info
### Модуль decort_rg
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-796 | Добавлен параметр `zones` и возвращаемое значение `zones`. |
| BANS-826 | Добавлен параметр `trunks` и возвращаемое значение `trunks`. |
| BANS-877 | Добавлено возвращаемое значение `storage_policy_ids`. |
| BANS-882 | Добавлен параметр `quotas.storage_policies`. |
### Модуль decort_account
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-789 | Добавлен параметр `default_zone_id` и возвращаемые значение `zoneIds`, `defaultZoneId`. |
| BANS-875 | Добавлены возвращаемые значения `storage_policy_ids` и `resourceLimits.storage_policies`. |
### Модуль decort_account_info
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-809 | Добавлено значение `MERGE` для параметра `computes.filter.tech_status`. |
| BANS-855 | Добавлены значения `SNAPCREATE`, `CLONING`, `ROLLBACK` для параметра `computes.filter.tech_status`. |
| BANS-876 | Добавлены возвращаемые значения `storage_policy_ids` и `resourceLimits.storage_policies`. |
| BANS-911 | У параметра `computes.filter.tech_status` добавлены значения `MIGRATING_IN` и `MIGRATING_OUT`. |
| BANS-903 | Добавлены новые возвращаемые значения в `audits`. |
### Модуль decort_rg
### Модуль decort_storage_policy
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-812 | Добавлен параметр `sdn_access_group_id` и возвращаемое значение `sdn_access_group_id`. |
| BANS-878 | Добавлен модуль `decort_storage_policy`. |
### Модуль decort_zone
### Модуль decort_user_info
| Идентификатор<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>задачи | Описание |
| --- | --- |
| BANS-825 | Добавлен модуль `decort_trunk` для получения информации о транковых портах. |
| BANS-883 | Добавлен модуль `decort_security_group`. |
### Модуль decort_snapshot
### Модуль decort_vins
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-808 | Добавлено значение `merge_aborted` для параметра `state`. |
| BANS-147 | Добавлен параметр `permanently` для удаления внутренней сети в корзину или безвозвратно. |
## Удалено
### Модуль decort_osimage
| Идентификатор<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
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-815 | Удалено значение по умолчанию для параметра `description`. |
| BANS-867 | Удалён параметр `iops`. |
| BANS-898 | Удалён параметр `disks.ids`. |
### Модуль decort_lb
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-815 | Удалено значение по умолчанию для параметра `description`. |
| BANS-320 | Удалён неиспользуемый параметр `ext_ip_addr`. |
### Модуль decort_k8s
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-804 | Удален параметр `started` в связи с переносом логики в параметр `state` (значение `started`). |
| BANS-199 | Удалён неиспользуемый параметр `quotas`. |
### Модуль decort_group
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-388 | Удалён неиспользуемый параметр `image_name`. |
## Исправлено
### Модуль decort_bservice
| Идентификатор<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>задачи | Описание |
| --- | --- |
| 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
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BANS-803 | Модуль завершал работу ошибкой Python при создании балансировщика с указанием параметра `backends` или `frontends`. |
| BANS-820 | Выполнение модуля с указанием параметра `vins_id` и без указания параметра `ext_net_id` вызывало создание балансировщика с некорректной сетевой конфигурацией, дальнейшее добавление конфигурации backend к которому завершалось ошибкой платформы. |
| BANS-799 | Скорректирована логика параметра целевого состояния `present`. Теперь состояние `present` соответствует тому, что балансировщик нагрузки существует, и не приводит к изменению состояния существующего балансировщика нагрузки. Также для параметра `state` значение по умолчанию `present` теперь только при создании балансировщика нагрузки. |
| BANS-752 | Исправлена ошибка, приводящая к отображению неактуальной информации о статусе балансировщика после его удаления. |
| BANS-754 | Исправлена логика работы с несуществующими объектами, по которой получение информации об объектах приводило к возникновению ошибок. |
| 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>задачи | Описание |
| --- | --- |
| BANS-817 | Модуль некорректно отслеживал завершение удаления и восстановления аккаунта. |
| BANS-663 | Исправлена ошибка передачи параметра `description` при создании внутренней сети. |
| BANS-174 | Исправлена логика, приводящая к невозможности задать внешнюю сеть для внутренней сети без внешней сети. |

@ -5,6 +5,7 @@
| Версия платформы | Версия модулей Ansible |
|:----------------:|:--------------------------:|
| 4.4.0 | 10.0.x |
| 4.4.0 build 963 | 9.0.x |
| 4.3.0 | 8.0.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)
if not arg_amodule.params['id']:
if not arg_amodule.params['rg_id']: # RG ID is not set -> locate RG by name -> need account ID
validated_acc_id, self.acc_info = self.account_find(arg_amodule.params['account_name'],
validated_acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'],
arg_amodule.params['account_id'])
if not validated_acc_id:
self.result['failed'] = True
@ -113,7 +113,7 @@ class decort_bservice(DecortController):
)
if 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
def action(self,d_state):
@ -176,7 +176,6 @@ class decort_bservice(DecortController):
),
state=dict(
type='str',
default='present',
choices=[
'absent',
'disabled',
@ -298,23 +297,28 @@ def main():
subj.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
subj.nop()
elif subj.bservice_info['status'] in ('ENABLED', 'DISABLED'):
elif subj.bservice_info['status'] in (
'ENABLED', 'DISABLED', 'CREATED',
):
if amodule.params['state'] == 'absent':
subj.destroy()
else:
subj.action(amodule.params['state'])
elif subj.bservice_info['status'] == "DESTROED":
elif subj.bservice_info['status'] == "DESTROYED":
if amodule.params['state'] in ('present','enabled'):
subj.create()
subj.action(amodule.params['state'])
if amodule.params['state'] == 'absent':
subj.nop()
else:
if amodule.params['state'] == 'absent':
state = amodule.params['state']
if state is None:
state = 'present'
if state == 'absent':
subj.nop()
if amodule.params['state'] in ('present','started'):
if state in ('present','started'):
subj.create()
elif amodule.params['state'] in ('stopped', 'disabled','enabled'):
elif state in ('stopped', 'disabled','enabled'):
subj.error()
if subj.result['failed']:

@ -41,6 +41,8 @@ class decort_disk(DecortController):
validated_acc_id, validated_acc_info = self.account_find(
arg_amodule.params['account_name'],
arg_amodule.params['account_id'])
self.acc_id = validated_acc_id
self._acc_info = validated_acc_info
if not validated_acc_id:
self.result['changed'] = False
self.result['msg'] = (
@ -51,8 +53,6 @@ class decort_disk(DecortController):
)
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(
disk_id=arg_amodule.params['id'],
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_info = validated_disk_facts
def create(self):
if self.disk_id:
self.acc_id = validated_disk_facts['accountId']
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
self.disk_id = self.disk_create(accountId=self.acc_id,
name = self.amodule.params['name'],
description=self.amodule.params['description'],
size=self.amodule.params['size'],
iops=self.amodule.params['iops'],
sep_id=self.amodule.params['sep_id'],
pool=self.amodule.params['pool'],
def create(self):
self.disk_id = self.disk_create(
accountId=self.acc_id,
name = self.amodule.params['name'],
description=self.amodule.params['description'],
size=self.amodule.params['size'],
sep_id=self.amodule.params['sep_id'],
pool=self.amodule.params['pool'],
storage_policy_id=self.aparams['storage_policy_id'],
)
#IO tune
if self.amodule.params['limitIO']:
@ -115,14 +121,25 @@ class decort_disk(DecortController):
#raise Exception(self.amodule.params['shareable'])
if self.amodule.params['shareable'] != self.disk_info['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
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'],
permanently=self.amodule.params['permanently'],
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
def rename(self):
@ -175,6 +192,8 @@ class decort_disk(DecortController):
ret_dict['iotune'] = self.disk_info['iotune']
ret_dict['size_available'] = self.disk_info['sizeAvailable']
ret_dict['size_used'] = self.disk_info['sizeUsed']
ret_dict['storage_policy_id'] = self.disk_info['storage_policy_id']
ret_dict['to_clean'] = self.disk_info['to_clean']
return ret_dict
@ -219,10 +238,6 @@ class decort_disk(DecortController):
size=dict(
type='int',
),
iops=dict(
type='int',
default=2000,
),
limitIO=dict(
type='dict',
options=dict(
@ -287,6 +302,9 @@ class decort_disk(DecortController):
'present',
],
),
storage_policy_id=dict(
type='int',
),
),
supports_check_mode=True,
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():
decon = decort_disk()
amodule = decon.amodule
@ -339,7 +424,7 @@ def main():
if decon.result['failed']:
amodule.fail_json(**decon.result)
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.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)

@ -36,10 +36,14 @@ class decort_group(DecortController):
group_id=arg_amodule.params['id'],
group_name=arg_amodule.params['name'],
)
self.acc_id = self.bservice_info['accountId']
self.rg_id = self.bservice_info['rgId']
if self.group_id:
self.group_should_exist = True
self.check_amodule_args_for_change()
else:
self.check_amodule_args_for_create()
return
def nop(self):
@ -84,6 +88,17 @@ class decort_group(DecortController):
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(
bs_id=self.bservice_id,
arg_name=self.amodule.params['name'],
@ -92,14 +107,18 @@ class decort_group(DecortController):
arg_ram=self.amodule.params['ram'],
arg_boot_disk=self.amodule.params['boot_disk'],
arg_image_id=self.amodule.params['image_id'],
arg_driver=self.amodule.params['driver'],
arg_role=self.amodule.params['role'],
arg_network=self.amodule.params['networks'],
arg_timeout=self.amodule.params['timeoutStart'],
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_should_exist = True
@ -196,6 +215,7 @@ class decort_group(DecortController):
ret_dict['techStatus'] = self.group_info['techStatus']
ret_dict['state'] = self.group_info['status']
ret_dict['Computes'] = self.group_info['computes']
ret_dict['driver'] = self.group_info['driver']
return ret_dict
@property
@ -229,17 +249,6 @@ class decort_group(DecortController):
image_id=dict(
type='int',
),
image_name=dict(
type='str',
),
driver=dict(
type='str',
choices=[
'KVM_X86',
'SVA_KVM_X86',
],
default='KVM_X86',
),
boot_disk=dict(
type='int',
),
@ -287,17 +296,16 @@ class decort_group(DecortController):
'i440fx',
]
),
storage_policy_id=dict(
type='int',
),
driver=dict(
type='str',
),
),
supports_check_mode=True,
required_one_of=[
('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 (
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
self.message(
@ -335,9 +343,71 @@ class decort_group(DecortController):
)
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:
self.exit(fail=True)
def main():
subj = decort_group()
amodule = subj.amodule
@ -359,8 +429,7 @@ def main():
if subj.group_id:
if subj.group_info['status'] in ("DELETING","DESTROYNG","CREATING","DESTROYING",
"ENABLING","DISABLING","RESTORING","MODELED",
"DISABLED","DESTROYED"):
"ENABLING","DISABLING","RESTORING","MODELED","DESTROYED"):
subj.error()
elif subj.group_info['status'] in ("DELETED","DESTROYED"):
if amodule.params['state'] == 'absent':

@ -166,34 +166,36 @@ class decort_k8s(DecortController):
if wg[param] is None:
wg[param] = default_value
k8s_id = self.k8s_provision(self.amodule.params['name'],
self.amodule.params['k8ci_id'],
self.amodule.params['rg_id'],
self.amodule.params['vins_id'],
self.amodule.params['network_plugin'],
self.amodule.params['master_count'],
self.amodule.params['master_cpu'],
self.amodule.params['master_ram'],
self.amodule.params['master_disk'],
self.amodule.params['master_sepid'],
self.amodule.params['master_pool'],
target_wgs[0],
self.amodule.params['extnet_id'],
self.amodule.params['with_lb'],
self.amodule.params['ha_lb'],
self.amodule.params['additionalSANs'],
self.amodule.params['init_conf'],
self.amodule.params['cluster_conf'],
self.amodule.params['kublet_conf'],
self.amodule.params['kubeproxy_conf'],
self.amodule.params['join_conf'],
self.amodule.params['oidc_cert'],
self.amodule.params['description'],
self.amodule.params['extnet_only'],
master_chipset,
lb_sysctl=self.amodule.params['lb_sysctl'],
zone_id=self.aparams['zone_id'],
)
k8s_id = self.k8s_provision(
self.amodule.params['name'],
self.amodule.params['k8ci_id'],
self.amodule.params['rg_id'],
self.amodule.params['vins_id'],
self.amodule.params['network_plugin'],
self.amodule.params['master_count'],
self.amodule.params['master_cpu'],
self.amodule.params['master_ram'],
self.amodule.params['master_disk'],
self.amodule.params['master_sepid'],
self.amodule.params['master_pool'],
target_wgs[0],
self.amodule.params['extnet_id'],
self.amodule.params['with_lb'],
self.amodule.params['ha_lb'],
self.amodule.params['additionalSANs'],
self.amodule.params['init_conf'],
self.amodule.params['cluster_conf'],
self.amodule.params['kublet_conf'],
self.amodule.params['kubeproxy_conf'],
self.amodule.params['join_conf'],
self.amodule.params['oidc_cert'],
self.amodule.params['description'],
self.amodule.params['extnet_only'],
master_chipset=master_chipset,
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 k8s_id == 0:
@ -241,6 +243,12 @@ class decort_k8s(DecortController):
)
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:
# K8s info updating
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
@ -275,9 +283,6 @@ class decort_k8s(DecortController):
type='str',
default='',
),
quotas=dict(
type='dict',
),
state=dict(
type='str',
default='present',
@ -448,6 +453,9 @@ class decort_k8s(DecortController):
zone_id=dict(
type='int',
),
storage_policy_id=dict(
type='int',
),
),
supports_check_mode=True,
required_one_of=[
@ -499,6 +507,32 @@ class decort_k8s(DecortController):
'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:
self.exit(fail=True)
@ -540,6 +574,26 @@ class decort_k8s(DecortController):
if self.check_aparam_zone_id() is False:
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:
self.exit(fail=True)

@ -56,6 +56,7 @@ class decort_kvmvm(DecortController):
comp_id=self.aparams['clone_from']['id'],
)
)
self.rg_id = self.vm_to_clone_info['rgId']
if not self.vm_to_clone_id:
self.message(
f'Check for parameter "clone_from.id" failed: '
@ -73,7 +74,7 @@ class decort_kvmvm(DecortController):
clone_id, clone_dict, _ = self.compute_find(
comp_name=self.aparams['name'],
rg_id=self.vm_to_clone_info['rgId'],
rg_id=self.rg_id,
)
self.check_amodule_args_for_clone(
clone_id=clone_id,
@ -389,7 +390,7 @@ class decort_kvmvm(DecortController):
Compute instance with the specified characteristics into the target Resource Group.
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
self.check_amodule_argument('cpu')
self.check_amodule_argument('ram')
@ -429,27 +430,15 @@ class decort_kvmvm(DecortController):
image_id, image_facts = None, None
if self.aparam_image:
# either image_name or image_id must be present
if (
self.check_amodule_argument('image_id', abort=False)
and self.amodule.params['image_id'] > 0
):
# 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=self.amodule.params['image_id'],
image_name="",
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']:
# 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
# NOTE: KVM VM is created in HALTED state and must be explicitly started
self.comp_id = self.kvmvm_provision(rg_id=self.rg_id,
comp_name=self.amodule.params['name'],
cpu=self.amodule.params['cpu'], ram=self.amodule.params['ram'],
boot_disk_size=validated_bdisk_size,
image_id=image_id,
description=self.amodule.params['description'],
userdata=cloud_init_params,
sep_id=self.amodule.params['sep_id' ] if "sep_id" in self.amodule.params else None,
pool_name=self.amodule.params['pool'] if "pool" in self.amodule.params else None,
start_on_create=start_compute,
chipset=chipset,
cpu_pin=cpu_pin,
hp_backed=hp_backed,
numa_affinity=numa_affinity,
preferred_cpu_cores=self.amodule.params['preferred_cpu_cores'],
boot_mode=boot_mode,
boot_loader_type=loader_type,
network_interface_naming=network_interface_naming,
hot_resize=hot_resize,
zone_id=self.aparams['zone_id'],)
self.comp_id = self.kvmvm_provision(
rg_id=self.rg_id,
comp_name=self.amodule.params['name'],
cpu=self.amodule.params['cpu'],
ram=self.amodule.params['ram'],
boot_disk_size=validated_bdisk_size,
image_id=image_id,
description=self.amodule.params['description'],
userdata=cloud_init_params,
sep_id=self.amodule.params['sep_id' ] if "sep_id" in self.amodule.params else None,
pool_name=self.amodule.params['pool'] if "pool" in self.amodule.params else None,
start_on_create=start_compute,
chipset=chipset,
cpu_pin=cpu_pin,
hp_backed=hp_backed,
numa_affinity=numa_affinity,
preferred_cpu_cores=self.amodule.params['preferred_cpu_cores'],
boot_mode=boot_mode,
boot_loader_type=loader_type,
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
# 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:
self.compute_disks(
comp_dict=self.comp_info,
aparam_disks=self.amodule.params['disks'],
aparam_disks_dict=self.amodule.params['disks'],
)
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.
"""
self.compute_delete(comp_id=self.comp_id, permanently=True)
self.comp_info['status'] = 'DESTROYED'
self.comp_should_exist = False
self.comp_id, self.comp_info, _ = self._compute_get_by_id(self.comp_id)
return
def restore(self):
@ -676,7 +669,7 @@ class decort_kvmvm(DecortController):
if self.amodule.params['disks'] is not None:
self.compute_disks(
comp_dict=self.comp_info,
aparam_disks=self.amodule.params['disks'],
aparam_disks_dict=self.amodule.params['disks'],
)
aparam_boot = self.amodule.params['boot']
@ -695,6 +688,59 @@ class decort_kvmvm(DecortController):
if 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.amodule.params['cpu'], self.amodule.params['ram'],
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
@property
@ -773,6 +841,7 @@ class decort_kvmvm(DecortController):
'boot.loader_type': 'loaderType',
'network_interface_naming': 'networkInterfaceNaming',
'hot_resize': 'hotResize',
'os_version': 'os_version',
}
def get_nested_value(
@ -814,6 +883,14 @@ class decort_kvmvm(DecortController):
)
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('.', '_')] = (
aparam_value
)
@ -837,7 +914,6 @@ class decort_kvmvm(DecortController):
cpu="",
ram="",
disk_size=0,
data_disks=[], # IDs of attached data disks; this list can be emty
state="CHECK_MODE",
tech_status="",
account_id=0,
@ -898,18 +974,19 @@ class decort_kvmvm(DecortController):
elif iface['connType'] == "VLAN": # This is direct external network connection
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['ram'] = self.comp_info['ram']
ret_dict['image_id'] = self.comp_info['imageId']
for ddisk in self.comp_info['disks']:
if ddisk['type'] == 'B':
ret_dict['disks'] = self.comp_info['disks']
for disk in ret_dict['disks']:
if disk['type'] == 'B':
# if it is a boot disk - store its size
ret_dict['disk_size'] = ddisk['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['disk_size'] = disk['sizeMax']
ret_dict['chipset'] = self.comp_info['chipset']
@ -960,15 +1037,30 @@ class decort_kvmvm(DecortController):
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
def check_amodule_args_for_create(self):
check_errors = False
# Check for unacceptable parameters for a blank Compute
if (
self.aparams['image_id'] is not None
or self.aparams['image_name'] is not None
):
if self.aparams['image_id'] is not None:
self.aparam_image = True
for param in (
'network_interface_naming',
@ -1017,7 +1109,7 @@ class decort_kvmvm(DecortController):
check_errors = True
self.message(
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}.'
)
@ -1029,7 +1121,7 @@ class decort_kvmvm(DecortController):
check_errors = True
self.message(
'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.'
)
@ -1072,6 +1164,39 @@ class decort_kvmvm(DecortController):
if self.check_aparam_networks_trunk() is False:
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:
self.exit(fail=True)
@ -1114,6 +1239,19 @@ class decort_kvmvm(DecortController):
'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(
@ -1142,9 +1280,24 @@ class decort_kvmvm(DecortController):
],
default='update',
),
ids=dict(
objects=dict(
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(
type='int',
),
image_name=dict(
type='str',
),
name=dict(
type='str',
),
@ -1190,6 +1340,16 @@ class decort_kvmvm(DecortController):
mac=dict(
type='str',
),
security_group_ids=dict(
type='list',
elements='int',
),
security_group_mode=dict(
type='bool',
),
enabled=dict(
type='bool',
),
),
required_if=[
('type', 'VINS', ('id',)),
@ -1197,7 +1357,7 @@ class decort_kvmvm(DecortController):
('type', 'VFNIC', ('id',)),
('type', 'DPDK', ('id',)),
('type', 'TRUNK', ('id',)),
('type', 'SDN', ('id', 'mac')),
('type', 'SDN', ('id',)),
],
),
network_order_changing=dict(
@ -1326,6 +1486,16 @@ class decort_kvmvm(DecortController):
('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(
@ -1368,6 +1538,34 @@ class decort_kvmvm(DecortController):
get_snapshot_merge_status=dict(
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,
required_one_of=[
@ -1404,30 +1602,6 @@ class decort_kvmvm(DecortController):
aparam_boot = self.amodule.params['boot']
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_boot_disk_id = aparam_boot['disk_id']
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}.'
)
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 (
not comp_info['imageId']
and self.amodule.params['state'] in (
@ -1574,24 +1817,24 @@ class decort_kvmvm(DecortController):
'VM must be started to get console url.'
)
aparam_disks = self.aparams['disks']
if aparam_disks is not None:
aparam_disks_ids = aparam_disks['ids']
aparam_disks_dict = self.aparams['disks']
if aparam_disks_dict is not None:
aparam_disks = aparam_disks_dict.get('objects', [])
aparam_disks_ids = [disk['id'] for disk in aparam_disks]
comp_boot_disk_id = None
for comp_disk in self.comp_info['disks']:
if comp_disk['type'] == 'B':
comp_boot_disk_id = comp_disk['id']
break
disks_to_detach = []
match aparam_disks['mode']:
match aparam_disks_dict['mode']:
case 'detach' | 'delete':
disks_to_detach = aparam_disks_ids
case 'match':
comp_disk_ids = {
disk['id'] for disk in self.comp_info['disks']
}
disks = set(aparam_disks_ids)
disks_to_detach = comp_disk_ids - disks
disks_to_detach = comp_disk_ids - set(aparam_disks_ids)
if (
comp_boot_disk_id is not None
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.'
)
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 (
(
self.aparams['cpu'] is not None
@ -1637,11 +1892,72 @@ class decort_kvmvm(DecortController):
aparam_networks = self.aparams['networks']
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}
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.check_aparam_networks_trunk() is False:
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:
self.exit(fail=True)
@ -1731,6 +2047,9 @@ class decort_kvmvm(DecortController):
snapshot_timestamp=snapshot_timestamp,
snapshot_name=snapshot_name,
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
@ -1812,6 +2131,130 @@ class decort_kvmvm(DecortController):
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(
self,
trunk_networks: list,
@ -1958,7 +2401,7 @@ def main():
amodule = subj.amodule
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
subj.error() # was subj.nop()
elif subj.comp_info['status'] in ("ENABLED", "DISABLED"):

@ -42,8 +42,8 @@ class decort_lb(DecortController):
if not self.lb_id:
self.result['failed'] = True
self.result['msg'] = "Specified LB ID {} not found."\
.format(arg_amodule.params['lb _id'])
self.fail_json(**self.result)
.format(arg_amodule.params['lb_id'])
self.amodule.fail_json(**self.result)
self.rg_id = self.lb_facts['rgId']
self.vins_id = self.lb_facts['vinsId']
@ -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="")
if not self.rg_id:
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.acc_id = self.rg_facts['accountId']
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
if not arg_amodule.params['rg_name']:
self.result['failed'] = True
self.result['msg'] = ("RG name must be specified with account present")
@ -163,8 +162,9 @@ class decort_lb(DecortController):
def delete(self):
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
def nop(self):
"""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
@ -252,10 +252,6 @@ class decort_lb(DecortController):
type='int',
default=0,
),
ext_ip_addr=dict(
type='str',
default='',
),
state=dict(
type='str',
choices=[
@ -411,10 +407,10 @@ def main():
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
if decon.result['changed'] and amodule.params['state'] != 'absent':
_, decon.lb_facts = decon.lb_find(decon.lb_id)
if decon.lb_id:
decon.result['facts'] = decon.package_facts(amodule.check_mode)
if decon.result['changed']:
_, decon.lb_facts = decon.lb_find(lb_id=decon.lb_id)
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main()

@ -22,7 +22,8 @@ class decort_osimage(DecortController):
self.validated_virt_image_id = 0
self.validated_image_name = amodule.params['image_name']
self.validated_virt_image_name = None
self.validated_virt_image_id = amodule.params['virt_id']
self.image_info: dict
self.virt_image_info: dict
if amodule.params['account_name']:
self.validated_account_id, _ = self.account_find(amodule.params['account_name'])
else:
@ -34,23 +35,51 @@ class decort_osimage(DecortController):
self.result['changed'] = False
self.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name'])
amodule.fail_json(**self.result)
self.acc_id = self.validated_account_id
if amodule.params['virt_id'] != 0 and amodule.params['virt_name']:
self.validated_virt_image_id, image_facts =\
if (
self.aparams['virt_id'] != 0
or self.aparams['virt_name'] is not None
):
self.validated_virt_image_id, self.virt_image_info = (
self.decort_virt_image_find(amodule)
if (self.validated_virt_image_id and
amodule.params['virt_name'] != image_facts['name']):
self.decort_virt_image_rename(amodule)
self.result['msg'] = 'Virtual image renamed successfully'
elif amodule.params['image_id'] != 0 and amodule.params['image_name']:
self.validated_image_id, image_facts = self.decort_image_find(amodule)
if (self.validated_image_id and
amodule.params['image_name'] != image_facts['name']):
decort_osimage.decort_image_rename(self,amodule)
self.result['msg'] = ("Image renamed successfully")
)
if self.virt_image_info:
_, linked_image_info = self._image_get_by_id(
image_id=self.virt_image_info['linkTo']
)
self.acc_id = linked_image_info['accountId']
if (
self.aparams['virt_name'] is not None
and self.aparams['virt_name']
!= self.virt_image_info['name']
):
self.decort_virt_image_rename(amodule)
self.result['msg'] = 'Virtual image renamed successfully'
elif (
self.aparams['image_id'] != 0
or self.aparams['image_name'] is not None
):
self.validated_image_id, self.image_info = (
self.decort_image_find(amodule)
)
if self.image_info:
self.acc_id = self.image_info['accountId']
if (
amodule.params['image_name']
and amodule.params['image_name'] != self.image_info['name']
):
decort_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):
# function that finds the OS image
@ -121,21 +150,22 @@ class decort_osimage(DecortController):
)
# function that creates OS image
image_facts = self.image_create(img_name=self.validated_image_name,
url=amodule.params['url'],
gid=amodule.params['gid'],
boot_mode=boot_mode,
boot_loader_type=loader_type,
hot_resize=hot_resize,
username=amodule.params['image_username'],
password=amodule.params['image_password'],
account_id=self.validated_account_id,
usernameDL=amodule.params['usernameDL'],
passwordDL=amodule.params['passwordDL'],
sepId=amodule.params['sepId'],
poolName=amodule.params['poolName'],
drivers=amodule.params['drivers'],
network_interface_naming=network_interface_naming)
image_facts = self.image_create(
img_name=self.validated_image_name,
url=amodule.params['url'],
boot_mode=boot_mode,
boot_loader_type=loader_type,
hot_resize=hot_resize,
username=amodule.params['image_username'],
password=amodule.params['image_password'],
account_id=self.validated_account_id,
usernameDL=amodule.params['usernameDL'],
passwordDL=amodule.params['passwordDL'],
sepId=amodule.params['sepId'],
poolName=amodule.params['poolName'],
network_interface_naming=network_interface_naming,
storage_policy_id=amodule.params['storage_policy_id'],
)
self.result['changed'] = True
return image_facts
@ -151,8 +181,9 @@ class decort_osimage(DecortController):
def decort_image_delete(self,amodule):
# function that removes an image
self.image_delete(imageId=amodule.image_id_delete)
self.result['changed'] = True
self.result['msg'] = ("Image '{}' deleted").format(amodule.image_id_delete)
_, image_facts = decort_osimage._image_get_by_id(self, 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):
# function that creates a virtual image
@ -231,6 +262,8 @@ class decort_osimage(DecortController):
'networkInterfaceNaming'
]
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
@property
@ -273,17 +306,9 @@ class decort_osimage(DecortController):
'present',
],
),
drivers=dict(
type='str',
default='KVM_X86',
),
url=dict(
type='str',
),
gid=dict(
type='int',
default=0,
),
sepId=dict(
type='int',
default=0,
@ -333,10 +358,102 @@ class decort_osimage(DecortController):
'eth',
],
),
storage_policy_id=dict(
type='int',
),
),
supports_check_mode=True,
)
def check_amodule_args_for_change(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if (
aparam_storage_policy_id is not None
and aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_change_virt_image(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if (
aparam_storage_policy_id is not None
and (
aparam_storage_policy_id
!= self.virt_image_info['storage_policy_id']
)
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id can not be changed in virtual image'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create_image(self):
check_errors = False
aparam_account_id = self.aparams['account_id']
if aparam_account_id is None:
check_errors = True
self.message(
msg='Check for parameter "account_id" failed: '
'account_id must be specified when creating '
'a new image'
)
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id must be specified when creating '
'a new image'
)
elif (
aparam_storage_policy_id
not in self.acc_info['storage_policy_ids']
):
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
f'Account ID {self.acc_id} does not have access to '
f'storage_policy_id {aparam_storage_policy_id}'
)
if check_errors:
self.exit(fail=True)
def check_amodule_args_for_create_virt_image(self):
check_errors = False
aparam_storage_policy_id = self.aparams['storage_policy_id']
if aparam_storage_policy_id is not None:
check_errors = True
self.message(
msg='Check for parameter "storage_policy_id" failed: '
'storage_policy_id can not be specified when creating '
'virtual image'
)
if check_errors:
self.exit(fail=True)
def main():
decon = decort_osimage()
amodule = decon.amodule
@ -360,15 +477,31 @@ def main():
decon.result['msg'] = ("Cannot find OS image")
amodule.fail_json(**decon.result)
if decon.validated_virt_image_id and decon.target_image_id:
if decort_osimage.decort_osimage_package_facts(image_facts)['linkto'] != decon.target_image_id:
if decon.validated_virt_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)
decon.result['changed'] = True
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
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']:
image_id, image_facts = decort_osimage.decort_image_find(decon, amodule)
@ -385,13 +518,35 @@ def main():
decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
elif amodule.params['state'] == "absent" and decon.validated_image_id:
amodule.image_id_delete = decon.validated_image_id
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)
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:
# we failed to find the specified image - fail the module
decon.result['changed'] = False
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)

@ -102,16 +102,47 @@ class decort_rg(DecortController):
resources = self.rg_facts['Resources']['Reserved']
incorrect_quota = dict(Requested=dict(),
Reserved=dict(),)
query_key_map = dict(cpu='cpu',
ram='ram',
disk='disksize',
ext_ips='extips',
net_transfer='exttraffic',)
query_key_map = dict(
cpu='cpu',
ram='ram',
disk='disksize',
ext_ips='extips',
net_transfer='exttraffic',
storage_policies='policies',
)
if 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]]:
incorrect_quota['Requested'][quota_item]=self.amodule.params['quotas'][quota_item]
incorrect_quota['Reserved'][quota_item]=resources[query_key_map[quota_item]]
if quota_item == 'storage_policies':
rg_storage_policies = 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']:
self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota)
@ -243,6 +274,7 @@ class decort_rg(DecortController):
ret_dict['uniqPools'] = self.rg_facts['uniqPools']
ret_dict['description'] = self.rg_facts['desc']
ret_dict['sdn_access_group_id'] = self.rg_facts['sdn_access_group_id']
ret_dict['storage_policy_ids'] = self.rg_facts['storage_policy_ids']
return ret_dict

@ -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
],
),
zone_id=dict(
type='int',
),
),
),
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,
)
@ -390,6 +530,8 @@ class DecortUserInfo(DecortController):
self.AccountStatus(input_args_filter_status)
)
mapped_args['zone_id'] = input_args_filter['zone_id']
input_args_pagination = input_args['pagination']
if input_args_pagination:
mapped_args['page_number'] = input_args_pagination['number']
@ -552,6 +694,82 @@ class DecortUserInfo(DecortController):
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):
self.get_info()
self.exit()
@ -606,6 +824,52 @@ class DecortUserInfo(DecortController):
trunk_facts['ovs_bridge'] = trunk_facts.pop('ovsBridge')
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():
DecortUserInfo().run()

@ -170,7 +170,7 @@ class decort_vins(DecortController):
return
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'
return
def nop(self):
@ -320,6 +320,10 @@ class decort_vins(DecortController):
type='str',
default='',
),
permanently=dict(
type='bool',
default=False,
),
vins_id=dict(
type='int',
default=0,
@ -416,8 +420,9 @@ def main():
vins_should_exist = True
elif amodule.params['state'] == 'absent':
# destroy permanently
decon.delete()
vins_should_exist = False
if decon.amodule.params['permanently']:
decon.delete()
vins_should_exist = False
elif amodule.params['state'] == 'disabled':
decon.error()
vins_should_exist = False

@ -2,7 +2,7 @@
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
'''
@ -12,7 +12,7 @@ from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.decort_utils import DecortController
class DecortSnapshot(DecortController):
class DecortVMSnapshot(DecortController):
def __init__(self):
super().__init__(AnsibleModule(**self.amodule_init_args))
self.check_amodule_args()
@ -181,7 +181,7 @@ class DecortSnapshot(DecortController):
def main():
DecortSnapshot().run()
DecortVMSnapshot().run()
if __name__ == '__main__':

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