Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef4f57a1bd | ||
| cd6a835227 | |||
| 1c0e5cfdb3 | |||
| b2cb33e9a8 | |||
| d5ce634491 | |||
| 9d78742a10 | |||
| 1ab446e05d | |||
| ae986fa9e6 | |||
| 3eb7fb2791 |
31
CHANGELOG.md
31
CHANGELOG.md
@@ -1,10 +1,33 @@
|
|||||||
# Список изменений в версии 11.0.1
|
# Список изменений в версии 13.0.2
|
||||||
|
|
||||||
|
## Изменено
|
||||||
|
|
||||||
|
|
||||||
## Добавлено
|
## Добавлено
|
||||||
|
### Глобально
|
||||||
|
| Идентификатор<br>задачи | Описание |
|
||||||
|
| --- | --- |
|
||||||
|
| BANS-1463 | Добавлен модуль `decort_vm_folder_list`, позволяющий получить список папок для группировки ВМ в ресурсной группе. |
|
||||||
|
| BANS-1467 | Добавлен модуль `decort_vm_folder_access`, позволяющий управлять правами доступа к папке виртуальных машин. |
|
||||||
|
| BANS-1464 | Добавлен модуль `decort_vm_folder`, позволяющий привести к целевому состоянию и получить информацию о папке для группировки ВМ в ресурсной группе. |
|
||||||
|
|
||||||
|
### Модуль decort_vm
|
||||||
|
| Идентификатор<br>задачи | Описание |
|
||||||
|
| --- | --- |
|
||||||
|
| BANS-1466 | Добавлены параметр и возвращаемое значение `folder_id`. |
|
||||||
|
### Модуль decort_vm_list
|
||||||
|
| Идентификатор<br>задачи | Описание |
|
||||||
|
| --- | --- |
|
||||||
|
| BANS-1468 | Добавлено возвращаемое значение `folder_id`. |
|
||||||
|
| BANS-1476 | Добавлен параметр `include_descendants`. |
|
||||||
|
| BANS-1477 | Добавлен параметр `filter.folder_id` для фильтрации виртуальных машин по идентификатору папки для группировки ВМ в ресурсной группе. |
|
||||||
|
|
||||||
|
### Модуль decort_audit_list
|
||||||
|
| Идентификатор<br>задачи | Описание |
|
||||||
|
| --- | --- |
|
||||||
|
| BANS-1840 | Добавлено возвращаемое значение `vmfolder_id`. |
|
||||||
|
|
||||||
## Удалено
|
## Удалено
|
||||||
|
|
||||||
|
|
||||||
## Исправлено
|
## Исправлено
|
||||||
| Идентификатор<br>задачи | Описание |
|
|
||||||
| --- | --- |
|
|
||||||
| BANS-1173 | Модуль завершал работу ошибкой запроса к API при попытке добавить группу worker-узлов к существующему кластеру. |
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
| Версия платформы | Версия модулей Ansible |
|
| Версия платформы | Версия модулей Ansible |
|
||||||
|:----------------:|:--------------------------:|
|
|:----------------:|:--------------------------:|
|
||||||
|
| 4.7.0 | 13.0.x |
|
||||||
|
| 4.6.0 | 12.0.x |
|
||||||
| 4.5.0 | 11.0.x |
|
| 4.5.0 | 11.0.x |
|
||||||
| 4.4.0 | 10.0.x |
|
| 4.4.0 | 10.0.x |
|
||||||
| 4.4.0 build 963 | 9.0.x |
|
| 4.4.0 build 963 | 9.0.x |
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ module: decort_account
|
|||||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from typing import Iterable
|
from typing import Any, Iterable
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.decort_utils import DecortController
|
from ansible.module_utils.decort_utils import DecortController, sdk_types
|
||||||
|
|
||||||
|
|
||||||
class DecortAccount(DecortController):
|
class DecortAccount(DecortController):
|
||||||
@@ -23,7 +23,7 @@ class DecortAccount(DecortController):
|
|||||||
def amodule_init_args(self) -> dict:
|
def amodule_init_args(self) -> dict:
|
||||||
return self.pack_amodule_init_args(
|
return self.pack_amodule_init_args(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
access_emails=dict(
|
send_access_emails=dict(
|
||||||
type='bool',
|
type='bool',
|
||||||
),
|
),
|
||||||
acl=dict(
|
acl=dict(
|
||||||
@@ -45,8 +45,13 @@ class DecortAccount(DecortController):
|
|||||||
options=dict(
|
options=dict(
|
||||||
rights=dict(
|
rights=dict(
|
||||||
type='str',
|
type='str',
|
||||||
choices=['R', 'RCX', 'ARCXDU'],
|
choices=(
|
||||||
default='R',
|
sdk_types.AccessTypeForSet
|
||||||
|
._member_names_
|
||||||
|
),
|
||||||
|
default=(
|
||||||
|
sdk_types.AccessTypeForSet.R.name
|
||||||
|
),
|
||||||
),
|
),
|
||||||
id=dict(
|
id=dict(
|
||||||
type='str',
|
type='str',
|
||||||
@@ -69,19 +74,19 @@ class DecortAccount(DecortController):
|
|||||||
quotas=dict(
|
quotas=dict(
|
||||||
type='dict',
|
type='dict',
|
||||||
options=dict(
|
options=dict(
|
||||||
cpu=dict(
|
cpu_count=dict(
|
||||||
type='int',
|
type='int',
|
||||||
),
|
),
|
||||||
disks_size=dict(
|
storage_size_gb=dict(
|
||||||
type='int',
|
type='int',
|
||||||
),
|
),
|
||||||
gpu=dict(
|
gpu_count=dict(
|
||||||
type='int',
|
type='int',
|
||||||
),
|
),
|
||||||
public_ip=dict(
|
ext_ip_count=dict(
|
||||||
type='int',
|
type='int',
|
||||||
),
|
),
|
||||||
ram=dict(
|
ram_size_mb=dict(
|
||||||
type='int',
|
type='int',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -135,7 +140,7 @@ class DecortAccount(DecortController):
|
|||||||
# Parameters or combinations of parameters that can
|
# Parameters or combinations of parameters that can
|
||||||
# cause changing the object.
|
# cause changing the object.
|
||||||
changing_params = [
|
changing_params = [
|
||||||
'access_emails',
|
'send_access_emails',
|
||||||
'acl',
|
'acl',
|
||||||
['id', 'name'],
|
['id', 'name'],
|
||||||
'quotas',
|
'quotas',
|
||||||
@@ -188,110 +193,308 @@ class DecortAccount(DecortController):
|
|||||||
self.acc_id, self._acc_info = self.account_find(
|
self.acc_id, self._acc_info = self.account_find(
|
||||||
account_name=self.aparams['name'],
|
account_name=self.aparams['name'],
|
||||||
account_id=self.aparams['id'],
|
account_id=self.aparams['id'],
|
||||||
resource_consumption=self.aparams['get_resource_consumption'],
|
)
|
||||||
)
|
|
||||||
# If this is a repeated getting info
|
# If this is a repeated getting info
|
||||||
else:
|
else:
|
||||||
# If check mode is enabled, there is no needed to
|
# If check mode is enabled, there is no needed to
|
||||||
# request info again
|
# request info again
|
||||||
if not self.amodule.check_mode:
|
if not self.amodule.check_mode:
|
||||||
self.acc_id, self._acc_info = self.account_find(
|
self.acc_id, self._acc_info = self.account_find(
|
||||||
account_id=self.acc_id,
|
account_id=self._acc_info.id,
|
||||||
resource_consumption=(
|
|
||||||
self.aparams['get_resource_consumption']
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
self.facts = self.acc_info
|
|
||||||
|
if self._acc_info is not None:
|
||||||
|
acc_info_dict = self.acc_info.model_dump()
|
||||||
|
if self.aparams['get_resource_consumption']:
|
||||||
|
resource_consumption_dict = (
|
||||||
|
self.api.cloudapi.account.get_resource_consumption(
|
||||||
|
account_id=self.acc_info.id,
|
||||||
|
).model_dump()
|
||||||
|
)
|
||||||
|
resource_consumption_dict.pop('id')
|
||||||
|
resource_consumption_dict.pop('quotas')
|
||||||
|
acc_info_dict['resource_consumption'] = (
|
||||||
|
resource_consumption_dict
|
||||||
|
)
|
||||||
|
self.facts = acc_info_dict
|
||||||
|
|
||||||
def change(self):
|
def change(self):
|
||||||
self.change_state()
|
self.change_state()
|
||||||
|
|
||||||
self.change_acl()
|
self.change_acl()
|
||||||
|
|
||||||
if self.account_update_args:
|
need_account_update_api_call: bool = False
|
||||||
self.account_update(account_id=self.acc_id,
|
|
||||||
**self.account_update_args)
|
sdk_param_send_access_emails = None
|
||||||
|
aparam_send_access_emails: bool | None = self.aparams[
|
||||||
|
'send_access_emails'
|
||||||
|
]
|
||||||
|
if (
|
||||||
|
aparam_send_access_emails is not None
|
||||||
|
and self.acc_info.send_access_emails != aparam_send_access_emails
|
||||||
|
):
|
||||||
|
sdk_param_send_access_emails = aparam_send_access_emails
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
sdk_param_name = None
|
||||||
|
aparam_name: str | None = self.aparams['name']
|
||||||
|
if (
|
||||||
|
self.aparams['id'] and aparam_name
|
||||||
|
and self.acc_info.name != aparam_name
|
||||||
|
):
|
||||||
|
sdk_param_name = aparam_name
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
sdk_param_cpu_count_quota = None
|
||||||
|
sdk_param_storage_size_quota_gb = None
|
||||||
|
sdk_param_gpu_count_quota = None
|
||||||
|
sdk_param_ext_ip_count_quota = None
|
||||||
|
sdk_param_ram_size_quota_mb = None
|
||||||
|
aparam_quotas: dict[str, Any] = self.aparams['quotas']
|
||||||
|
if aparam_quotas:
|
||||||
|
aparam_quotas_cpu_count: int | None = aparam_quotas['cpu_count']
|
||||||
|
if (
|
||||||
|
aparam_quotas_cpu_count is not None
|
||||||
|
and self.acc_info.quotas.cpu_count != aparam_quotas_cpu_count
|
||||||
|
):
|
||||||
|
sdk_param_cpu_count_quota = aparam_quotas_cpu_count
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
aparam_quotas_storage_size_gb: int | None = aparam_quotas[
|
||||||
|
'storage_size_gb'
|
||||||
|
]
|
||||||
|
if (
|
||||||
|
aparam_quotas_storage_size_gb is not None
|
||||||
|
and (
|
||||||
|
self.acc_info.quotas.storage_size_gb
|
||||||
|
!= aparam_quotas_storage_size_gb
|
||||||
|
)
|
||||||
|
):
|
||||||
|
sdk_param_storage_size_quota_gb = aparam_quotas_storage_size_gb
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
aparam_quotas_gpu_count: int | None = aparam_quotas['gpu_count']
|
||||||
|
if (
|
||||||
|
aparam_quotas_gpu_count is not None
|
||||||
|
and self.acc_info.quotas.gpu_count != aparam_quotas_gpu_count
|
||||||
|
):
|
||||||
|
sdk_param_gpu_count_quota = aparam_quotas_gpu_count
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
aparam_quotas_ext_ip_count: int | None = aparam_quotas[
|
||||||
|
'ext_ip_count'
|
||||||
|
]
|
||||||
|
if (
|
||||||
|
aparam_quotas_ext_ip_count is not None
|
||||||
|
and (
|
||||||
|
self.acc_info.quotas.ext_ip_count
|
||||||
|
!= aparam_quotas_ext_ip_count
|
||||||
|
)
|
||||||
|
):
|
||||||
|
sdk_param_ext_ip_count_quota = aparam_quotas_ext_ip_count
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
aparam_quotas_ram_size_mb: int | None = aparam_quotas[
|
||||||
|
'ram_size_mb'
|
||||||
|
]
|
||||||
|
if (
|
||||||
|
aparam_quotas_ram_size_mb is not None
|
||||||
|
and (
|
||||||
|
self.acc_info.quotas.ram_size_mb
|
||||||
|
!= aparam_quotas_ram_size_mb
|
||||||
|
)
|
||||||
|
):
|
||||||
|
sdk_param_ram_size_quota_mb = aparam_quotas_ram_size_mb
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
sdk_param_sep_pools = None
|
||||||
|
aparam_sep_pools: list[dict[str, Any]] = self.aparams['sep_pools']
|
||||||
|
if aparam_sep_pools is not None:
|
||||||
|
sep_pools: set[str] = set()
|
||||||
|
for sep in aparam_sep_pools:
|
||||||
|
sep_pool_names: list[str] = sep['pool_names']
|
||||||
|
for pool_name in sep_pool_names:
|
||||||
|
sep_pools.add(f'{sep["sep_id"]}_{pool_name}')
|
||||||
|
if set(self.acc_info.sep_pools) != sep_pools:
|
||||||
|
sdk_param_sep_pools = list(sep_pools)
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
sdk_param_description = None
|
||||||
|
aparam_desc: str | None = self.aparams['description']
|
||||||
|
if (
|
||||||
|
aparam_desc is not None
|
||||||
|
and self.acc_info.description != aparam_desc
|
||||||
|
):
|
||||||
|
sdk_param_description = aparam_desc
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
sdk_param_default_zone_id = None
|
||||||
|
aparam_default_zone_id: int | None = self.aparams['default_zone_id']
|
||||||
|
if (
|
||||||
|
aparam_default_zone_id is not None
|
||||||
|
and self.acc_info.default_zone_id != aparam_default_zone_id
|
||||||
|
):
|
||||||
|
sdk_param_default_zone_id = aparam_default_zone_id
|
||||||
|
need_account_update_api_call = True
|
||||||
|
|
||||||
|
if need_account_update_api_call:
|
||||||
|
OBJ = 'account'
|
||||||
|
|
||||||
|
self.sdk_checkmode(self.api.ca.account.update)(
|
||||||
|
account_id=self.acc_info.id,
|
||||||
|
cpu_count_quota=sdk_param_cpu_count_quota,
|
||||||
|
gpu_count_quota=sdk_param_gpu_count_quota,
|
||||||
|
name=sdk_param_name,
|
||||||
|
ext_ip_count_quota=sdk_param_ext_ip_count_quota,
|
||||||
|
ram_size_quota_mb=sdk_param_ram_size_quota_mb,
|
||||||
|
send_access_emails=sdk_param_send_access_emails,
|
||||||
|
storage_size_quota_gb=sdk_param_storage_size_quota_gb,
|
||||||
|
sep_pools=sdk_param_sep_pools,
|
||||||
|
description=sdk_param_description,
|
||||||
|
default_zone_id=sdk_param_default_zone_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
if sdk_param_send_access_emails is not None:
|
||||||
|
smth = 'sending access emails'
|
||||||
|
if sdk_param_send_access_emails:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_smth_enabled(
|
||||||
|
obj=OBJ,
|
||||||
|
id=self.acc_info.id,
|
||||||
|
smth=smth,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_smth_disabled(
|
||||||
|
obj=OBJ,
|
||||||
|
id=self.acc_info.id,
|
||||||
|
smth=smth,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if sdk_param_name is not None:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_renamed(
|
||||||
|
obj=OBJ,
|
||||||
|
id=self.acc_info.id,
|
||||||
|
new_name=sdk_param_name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
quotas = {
|
||||||
|
'CPU count quota': sdk_param_cpu_count_quota,
|
||||||
|
'storage size quota GB': sdk_param_storage_size_quota_gb,
|
||||||
|
'GPU count quota': sdk_param_gpu_count_quota,
|
||||||
|
'ext IP count quota': sdk_param_ext_ip_count_quota,
|
||||||
|
'RAM size quota MB': sdk_param_ram_size_quota_mb,
|
||||||
|
}
|
||||||
|
for q_name, q_value in quotas.items():
|
||||||
|
if q_value is not None:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_smth_changed(
|
||||||
|
obj=OBJ,
|
||||||
|
id=self.acc_info.id,
|
||||||
|
smth=q_name,
|
||||||
|
new_value=q_value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if sdk_param_default_zone_id is not None:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_smth_changed(
|
||||||
|
obj=OBJ,
|
||||||
|
id=self.acc_info.id,
|
||||||
|
smth='default_zone_id',
|
||||||
|
new_value=sdk_param_default_zone_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
|
||||||
def change_state(self):
|
def change_state(self):
|
||||||
match self._acc_info:
|
if self._acc_info is None:
|
||||||
case None:
|
self.message(self.MESSAGES.obj_not_found(obj=self.OBJ))
|
||||||
self.message(self.MESSAGES.obj_not_found(obj=self.OBJ))
|
match self.aparams:
|
||||||
match self.aparams:
|
case {'state': 'absent' | 'absent_permanently'}:
|
||||||
case {'state': 'absent' | 'absent_permanently'}:
|
pass
|
||||||
pass
|
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
||||||
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
self.exit(fail=True)
|
||||||
self.exit(fail=True)
|
elif self._acc_info.status == sdk_types.AccountStatus.DESTROYED:
|
||||||
case {'status': 'DESTROYED'}:
|
match self.aparams:
|
||||||
match self.aparams:
|
case {'state': 'absent' | 'absent_permanently'}:
|
||||||
case {'state': 'absent' | 'absent_permanently'}:
|
self.message(
|
||||||
self.message(
|
self.MESSAGES.obj_deleted(
|
||||||
self.MESSAGES.obj_deleted(
|
obj=self.OBJ,
|
||||||
obj=self.OBJ,
|
id=self.acc_info.id,
|
||||||
id=self.acc_id,
|
permanently=True,
|
||||||
permanently=True,
|
already=True,
|
||||||
already=True,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
)
|
||||||
self.message(
|
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
||||||
self.MESSAGES.obj_not_restored(obj=self.OBJ,
|
self.message(
|
||||||
id=self.acc_id)
|
self.MESSAGES.obj_not_restored(obj=self.OBJ,
|
||||||
)
|
id=self.acc_info.id)
|
||||||
self.exit(fail=True)
|
|
||||||
case {'status': 'DELETED'}:
|
|
||||||
match self.aparams:
|
|
||||||
case {'state': 'absent'}:
|
|
||||||
self.message(
|
|
||||||
self.MESSAGES.obj_deleted(
|
|
||||||
obj=self.OBJ,
|
|
||||||
id=self.acc_id,
|
|
||||||
permanently=False,
|
|
||||||
already=True,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
case {'state': 'absent_permanently'}:
|
self.exit(fail=True)
|
||||||
self.delete(permanently=True)
|
elif self._acc_info.status == sdk_types.AccountStatus.DELETED:
|
||||||
case {'state': 'confirmed' | 'present'}:
|
match self.aparams:
|
||||||
self.restore()
|
case {'state': 'absent'}:
|
||||||
case {'state': 'disabled'}:
|
self.message(
|
||||||
self.restore()
|
self.MESSAGES.obj_deleted(
|
||||||
self.disable()
|
obj=self.OBJ,
|
||||||
case {'status': 'CONFIRMED'}:
|
id=self.acc_info.id,
|
||||||
match self.aparams:
|
permanently=False,
|
||||||
case {'state': 'absent'}:
|
already=True,
|
||||||
self.delete()
|
)
|
||||||
case {'state': 'absent_permanently'}:
|
)
|
||||||
self.delete(permanently=True)
|
case {'state': 'absent_permanently'}:
|
||||||
case {'state': 'confirmed' | 'present'}:
|
self.delete(permanently=True)
|
||||||
pass
|
case {'state': 'confirmed' | 'present'}:
|
||||||
case {'state': 'disabled'}:
|
self.restore()
|
||||||
self.disable()
|
case {'state': 'disabled'}:
|
||||||
case {'status': 'DISABLED'}:
|
self.restore()
|
||||||
match self.aparams:
|
self.disable()
|
||||||
case {'state': 'absent'}:
|
elif self._acc_info.status == sdk_types.AccountStatus.CONFIRMED:
|
||||||
self.delete()
|
match self.aparams:
|
||||||
case {'state': 'absent_permanently'}:
|
case {'state': 'absent'}:
|
||||||
self.delete(permanently=True)
|
self.delete()
|
||||||
case {'state': 'confirmed'}:
|
case {'state': 'absent_permanently'}:
|
||||||
self.enable()
|
self.delete(permanently=True)
|
||||||
case {'state': 'present' | 'disabled'}:
|
case {'state': 'confirmed' | 'present'}:
|
||||||
pass
|
pass
|
||||||
|
case {'state': 'disabled'}:
|
||||||
|
self.disable()
|
||||||
|
elif self._acc_info.status == sdk_types.AccountStatus.DISABLED:
|
||||||
|
match self.aparams:
|
||||||
|
case {'state': 'absent'}:
|
||||||
|
self.delete()
|
||||||
|
case {'state': 'absent_permanently'}:
|
||||||
|
self.delete(permanently=True)
|
||||||
|
case {'state': 'confirmed'}:
|
||||||
|
self.enable()
|
||||||
|
case {'state': 'present' | 'disabled'}:
|
||||||
|
pass
|
||||||
|
|
||||||
def delete(self, permanently=False):
|
def delete(self, permanently=False):
|
||||||
self.account_delete(account_id=self.acc_id, permanently=permanently)
|
self.account_delete(
|
||||||
|
account_id=self.acc_info.id,
|
||||||
|
permanently=permanently
|
||||||
|
)
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
self.account_disable(account_id=self.acc_id)
|
self.sdk_checkmode(self.api.ca.account.disable)(
|
||||||
|
account_id=self.acc_info.id
|
||||||
|
)
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
self.account_enable(account_id=self.acc_id)
|
self.sdk_checkmode(self.api.ca.account.enable)(
|
||||||
|
account_id=self.acc_info.id
|
||||||
|
)
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
|
||||||
def restore(self):
|
def restore(self):
|
||||||
self.account_restore(account_id=self.acc_id)
|
self.account_restore(account_id=self.acc_info.id)
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
|
||||||
def change_acl(self):
|
def change_acl(self):
|
||||||
@@ -299,7 +502,7 @@ class DecortAccount(DecortController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
actual_users = {
|
actual_users = {
|
||||||
u['userGroupId']: u['right'] for u in self.acc_info['acl']
|
u.user_name: u.access_type for u in self.acc_info.acl
|
||||||
}
|
}
|
||||||
actual_users_ids = set(actual_users.keys())
|
actual_users_ids = set(actual_users.keys())
|
||||||
|
|
||||||
@@ -328,8 +531,8 @@ class DecortAccount(DecortController):
|
|||||||
aparams_users_ids.intersection(actual_users_ids)
|
aparams_users_ids.intersection(actual_users_ids)
|
||||||
upd_users = dict()
|
upd_users = dict()
|
||||||
for id in upd_users_ids:
|
for id in upd_users_ids:
|
||||||
if actual_users[id] == 'CXDRAU':
|
if actual_users[id] == sdk_types.AccessType.CXDRAU:
|
||||||
actual_user_rights = 'ARCXDU'
|
actual_user_rights = sdk_types.AccessTypeForSet.ARCXDU
|
||||||
else:
|
else:
|
||||||
actual_user_rights = actual_users[id]
|
actual_user_rights = actual_users[id]
|
||||||
|
|
||||||
@@ -341,68 +544,12 @@ class DecortAccount(DecortController):
|
|||||||
actual_users_ids.difference(aparams_users_ids)
|
actual_users_ids.difference(aparams_users_ids)
|
||||||
|
|
||||||
if del_users_ids or new_users or upd_users:
|
if del_users_ids or new_users or upd_users:
|
||||||
self.account_change_acl(account_id=self.acc_id,
|
self.account_change_acl(account_id=self.acc_info.id,
|
||||||
del_users=del_users_ids,
|
del_users=del_users_ids,
|
||||||
add_users=new_users,
|
add_users=new_users,
|
||||||
upd_users=upd_users)
|
upd_users=upd_users)
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
|
||||||
@property
|
|
||||||
def account_update_args(self) -> dict:
|
|
||||||
result_args = dict()
|
|
||||||
|
|
||||||
aparam_access_emails = self.aparams['access_emails']
|
|
||||||
if (aparam_access_emails is not None
|
|
||||||
and self.acc_info['sendAccessEmails'] != aparam_access_emails):
|
|
||||||
result_args['access_emails'] = aparam_access_emails
|
|
||||||
|
|
||||||
aparam_name = self.aparams['name']
|
|
||||||
if (self.aparams['id'] and aparam_name
|
|
||||||
and self.acc_info['name'] != aparam_name):
|
|
||||||
result_args['name'] = aparam_name
|
|
||||||
|
|
||||||
aparam_quotas = self.aparams['quotas']
|
|
||||||
if aparam_quotas:
|
|
||||||
quotas_naming = [
|
|
||||||
['cpu', 'CU_C', 'cpu_quota'],
|
|
||||||
['disks_size', 'CU_DM', 'disks_size_quota'],
|
|
||||||
['gpu', 'gpu_units', 'gpu_quota'],
|
|
||||||
['public_ip', 'CU_I', 'public_ip_quota'],
|
|
||||||
['ram', 'CU_M', 'ram_quota'],
|
|
||||||
]
|
|
||||||
for aparam, info_key, result_arg in quotas_naming:
|
|
||||||
current_value = int(self.acc_info['resourceLimits'][info_key])
|
|
||||||
if (aparam_quotas[aparam] is not None
|
|
||||||
and current_value != aparam_quotas[aparam]):
|
|
||||||
result_args[result_arg] = aparam_quotas[aparam]
|
|
||||||
|
|
||||||
aparam_sep_pools = self.aparams['sep_pools']
|
|
||||||
if aparam_sep_pools is not None:
|
|
||||||
sep_pools = set()
|
|
||||||
for sep in aparam_sep_pools:
|
|
||||||
for pool_name in sep['pool_names']:
|
|
||||||
sep_pools.add(
|
|
||||||
f'{sep["sep_id"]}_{pool_name}'
|
|
||||||
)
|
|
||||||
if set(self.acc_info['uniqPools']) != sep_pools:
|
|
||||||
result_args['sep_pools'] = sep_pools
|
|
||||||
|
|
||||||
aparam_desc = self.aparams['description']
|
|
||||||
if (
|
|
||||||
aparam_desc is not None
|
|
||||||
and self.acc_info['description'] != aparam_desc
|
|
||||||
):
|
|
||||||
result_args['description'] = aparam_desc
|
|
||||||
|
|
||||||
aparam_default_zone_id = self.aparams['default_zone_id']
|
|
||||||
if (
|
|
||||||
aparam_default_zone_id is not None
|
|
||||||
and self.acc_info['defaultZoneId'] != aparam_default_zone_id
|
|
||||||
):
|
|
||||||
result_args['default_zone_id'] = aparam_default_zone_id
|
|
||||||
|
|
||||||
return result_args
|
|
||||||
|
|
||||||
def check_aparam_default_zone_id(self) -> bool | None:
|
def check_aparam_default_zone_id(self) -> bool | None:
|
||||||
aparam_default_zone_id = self.aparams['default_zone_id']
|
aparam_default_zone_id = self.aparams['default_zone_id']
|
||||||
if aparam_default_zone_id is not None:
|
if aparam_default_zone_id is not None:
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class DecortAccountList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class DecortAuditList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ class decort_bservice(DecortController):
|
|||||||
|
|
||||||
validated_acc_id = 0
|
validated_acc_id = 0
|
||||||
validated_rg_id = 0
|
validated_rg_id = 0
|
||||||
self.bservice_info = None
|
|
||||||
self.is_bservice_stopped_or_will_be_stopped: None | bool = None
|
self.is_bservice_stopped_or_will_be_stopped: None | bool = None
|
||||||
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] == 0:
|
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] == 0:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = "Cannot manage Basic Services when its ID is 0 and name is empty."
|
self.result['msg'] = "Cannot manage Basic Services when its ID is 0 and name is empty."
|
||||||
self.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
if not arg_amodule.params['id']:
|
if not arg_amodule.params['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'],
|
||||||
@@ -36,33 +35,46 @@ class decort_bservice(DecortController):
|
|||||||
self.result['msg'] = ("Current user does not have access to the account ID {} / "
|
self.result['msg'] = ("Current user does not have access to the account ID {} / "
|
||||||
"name '{}' or non-existent account specified.").format(arg_amodule.params['account_id'],
|
"name '{}' or non-existent account specified.").format(arg_amodule.params['account_id'],
|
||||||
arg_amodule.params['account_name'])
|
arg_amodule.params['account_name'])
|
||||||
self.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
# fail the module -> exit
|
# fail the module -> exit
|
||||||
# now validate RG
|
# now validate RG
|
||||||
validated_rg_id, validated_rg_facts = self.rg_find(
|
validated_rg_id, validated_rg_model = self.rg_find(
|
||||||
arg_account_id=validated_acc_id,
|
arg_account_id=validated_acc_id,
|
||||||
arg_rg_id=arg_amodule.params['rg_id'],
|
arg_rg_id=arg_amodule.params['rg_id'],
|
||||||
arg_rg_name=arg_amodule.params['rg_name']
|
arg_rg_name=arg_amodule.params['rg_name']
|
||||||
)
|
)
|
||||||
if not validated_rg_id:
|
if not validated_rg_id or not validated_rg_model:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||||
arg_amodule.params['rg_name'])
|
arg_amodule.params['rg_name'])
|
||||||
self.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
|
|
||||||
arg_amodule.params['rg_id'] = validated_rg_id
|
arg_amodule.params['rg_id'] = validated_rg_id
|
||||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
arg_amodule.params['rg_name'] = validated_rg_model.name
|
||||||
validated_acc_id = validated_rg_facts['accountId']
|
validated_acc_id = validated_rg_model.account_id
|
||||||
|
|
||||||
self.bservice_id, self.bservice_info = self.bservice_find(
|
self.bservice_id, self._bservice_info = self.bservice_find(
|
||||||
validated_acc_id,
|
account_id=validated_acc_id,
|
||||||
validated_rg_id,
|
rg_id=validated_rg_id,
|
||||||
arg_amodule.params['name'],
|
bservice_name=arg_amodule.params['name'],
|
||||||
arg_amodule.params['id']
|
bservice_id=arg_amodule.params['id'],
|
||||||
|
fail_if_not_found=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.acc_id = validated_acc_id or self.bservice_info['accountId']
|
if arg_amodule.params['id'] and self.bservice_id == 0:
|
||||||
|
if arg_amodule.params['state'] == 'absent':
|
||||||
|
self.exit()
|
||||||
|
else:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='bservice',
|
||||||
|
id=arg_amodule.params['id'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
self.acc_id = validated_acc_id or self.bservice_info['account_id']
|
||||||
|
|
||||||
if self.bservice_id and self.bservice_info['status'] != 'DESTROYED':
|
if self.bservice_id and self.bservice_info['status'] != 'DESTROYED':
|
||||||
self.bservice_should_exist = True
|
self.bservice_should_exist = True
|
||||||
@@ -92,8 +104,8 @@ class decort_bservice(DecortController):
|
|||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
if self.bservice_id:
|
if self.bservice_id:
|
||||||
self.result['msg'] = ("Invalid target state '{}' requested for B-service ID {} in the "
|
self.result['msg'] = ("Invalid target state '{}' requested for B-service ID {} in the "
|
||||||
"current status '{}'.").format(self.bservice_id,
|
"current status '{}'.").format(self.amodule.params['state'],
|
||||||
self.amodule.params['state'],
|
self.bservice_id,
|
||||||
self.bservice_info['status'])
|
self.bservice_info['status'])
|
||||||
else:
|
else:
|
||||||
self.result['msg'] = ("Invalid target state '{}' requested for non-existent B-service name '{}' "
|
self.result['msg'] = ("Invalid target state '{}' requested for non-existent B-service name '{}' "
|
||||||
@@ -104,15 +116,15 @@ class decort_bservice(DecortController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
self.bservice_id = self.bservice_id = self.bservice_provision(
|
self.bservice_id = self.sdk_checkmode(self.api.ca.bservice.create)(
|
||||||
self.amodule.params['name'],
|
name=self.amodule.params['name'],
|
||||||
self.amodule.params['rg_id'],
|
rg_id=self.amodule.params['rg_id'],
|
||||||
self.amodule.params['sshuser'],
|
ssh_user_name=self.amodule.params['sshuser'],
|
||||||
self.amodule.params['sshkey'],
|
ssh_public_key=self.amodule.params['sshkey'],
|
||||||
zone_id=self.aparams['zone_id'],
|
zone_id=self.aparams['zone_id'],
|
||||||
)
|
)
|
||||||
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, self.aparams['state'])
|
self.bservice_state(self.bservice_info, self.aparams['state'])
|
||||||
|
|
||||||
self.bservice_should_exist = True
|
self.bservice_should_exist = True
|
||||||
@@ -122,9 +134,9 @@ class decort_bservice(DecortController):
|
|||||||
self.bservice_state(self.bservice_info,d_state)
|
self.bservice_state(self.bservice_info,d_state)
|
||||||
|
|
||||||
aparam_zone_id = self.aparams['zone_id']
|
aparam_zone_id = self.aparams['zone_id']
|
||||||
if aparam_zone_id is not None and aparam_zone_id != self.bservice_info['zoneId']:
|
if aparam_zone_id is not None and aparam_zone_id != self.bservice_info['zone_id']:
|
||||||
self.bservice_migrate_to_zone(
|
self.sdk_checkmode(self.api.cloudapi.bservice.migrate_to_zone)(
|
||||||
bs_id=self.bservice_id,
|
bservice_id=self.bservice_id,
|
||||||
zone_id=aparam_zone_id,
|
zone_id=aparam_zone_id,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -136,7 +148,10 @@ class decort_bservice(DecortController):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
self.bservice_delete(self.bservice_id)
|
self.sdk_checkmode(self.api.ca.bservice.delete)(
|
||||||
|
bservice_id=self.bservice_id,
|
||||||
|
permanently=True,
|
||||||
|
)
|
||||||
self.bservice_info['status'] = 'DELETED'
|
self.bservice_info['status'] = 'DELETED'
|
||||||
self.bservice_should_exist = False
|
self.bservice_should_exist = False
|
||||||
return
|
return
|
||||||
@@ -152,18 +167,9 @@ class decort_bservice(DecortController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if check_mode:
|
if check_mode:
|
||||||
# in check mode return immediately with the default values
|
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
ret_dict['id'] = self.bservice_info['id']
|
return self.bservice_info
|
||||||
ret_dict['name'] = self.bservice_info['name']
|
|
||||||
ret_dict['techStatus'] = self.bservice_info['techStatus']
|
|
||||||
ret_dict['state'] = self.bservice_info['status']
|
|
||||||
ret_dict['rg_id'] = self.bservice_info['rgId']
|
|
||||||
ret_dict['account_id'] = self.bservice_info['accountId']
|
|
||||||
ret_dict['groups'] = self.bservice_info['groups']
|
|
||||||
ret_dict['zone_id'] = self.bservice_info['zoneId']
|
|
||||||
return ret_dict
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amodule_init_args(self) -> dict:
|
def amodule_init_args(self) -> dict:
|
||||||
@@ -225,14 +231,14 @@ class decort_bservice(DecortController):
|
|||||||
|
|
||||||
self.is_bservice_stopped_or_will_be_stopped = (
|
self.is_bservice_stopped_or_will_be_stopped = (
|
||||||
(
|
(
|
||||||
self.bservice_info['techStatus'] == 'STOPPED'
|
self.bservice_info['tech_status'] == 'STOPPED'
|
||||||
and (
|
and (
|
||||||
self.aparams['state'] is None
|
self.aparams['state'] is None
|
||||||
or self.aparams['state'] in ('present', 'stopped')
|
or self.aparams['state'] in ('present', 'stopped')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
self.bservice_info['techStatus'] != 'STOPPED'
|
self.bservice_info['tech_status'] != 'STOPPED'
|
||||||
and self.aparams['state'] == 'stopped'
|
and self.aparams['state'] == 'stopped'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -241,7 +247,7 @@ class decort_bservice(DecortController):
|
|||||||
check_errors = True
|
check_errors = True
|
||||||
if (
|
if (
|
||||||
self.aparams['zone_id'] is not None
|
self.aparams['zone_id'] is not None
|
||||||
and self.aparams['zone_id'] != self.bservice_info['zoneId']
|
and self.aparams['zone_id'] != self.bservice_info['zone_id']
|
||||||
and not self.is_bservice_stopped_or_will_be_stopped
|
and not self.is_bservice_stopped_or_will_be_stopped
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
@@ -267,7 +273,10 @@ class decort_bservice(DecortController):
|
|||||||
|
|
||||||
if self.amodule.check_mode:
|
if self.amodule.check_mode:
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
if self.bservice_id:
|
if (
|
||||||
|
self.bservice_id
|
||||||
|
or (self.aparams['rg_id'] and self.aparams['name'])
|
||||||
|
):
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['facts'] = self.package_facts(amodule.check_mode)
|
self.result['facts'] = self.package_facts(amodule.check_mode)
|
||||||
amodule.exit_json(**self.result)
|
amodule.exit_json(**self.result)
|
||||||
@@ -306,10 +315,12 @@ class decort_bservice(DecortController):
|
|||||||
else:
|
else:
|
||||||
self.action(amodule.params['state'])
|
self.action(amodule.params['state'])
|
||||||
elif self.bservice_info['status'] == "DESTROYED":
|
elif self.bservice_info['status'] == "DESTROYED":
|
||||||
if amodule.params['state'] in ('present','enabled'):
|
if amodule.params['state'] == 'present':
|
||||||
self.create()
|
self.create()
|
||||||
self.action(amodule.params['state'])
|
self.action(amodule.params['state'])
|
||||||
if amodule.params['state'] == 'absent':
|
elif amodule.params['state'] in ['enabled', 'disabled']:
|
||||||
|
self.error()
|
||||||
|
elif amodule.params['state'] == 'absent':
|
||||||
self.nop()
|
self.nop()
|
||||||
else:
|
else:
|
||||||
state = amodule.params['state']
|
state = amodule.params['state']
|
||||||
@@ -326,7 +337,9 @@ class decort_bservice(DecortController):
|
|||||||
amodule.fail_json(**self.result)
|
amodule.fail_json(**self.result)
|
||||||
else:
|
else:
|
||||||
if self.bservice_should_exist:
|
if self.bservice_should_exist:
|
||||||
_, self.bservice_info = self.bservice_get_by_id(self.bservice_id)
|
self._bservice_info = self.bservice_get_by_id(
|
||||||
|
self.bservice_id
|
||||||
|
)
|
||||||
self.result['facts'] = self.package_facts(amodule.check_mode)
|
self.result['facts'] = self.package_facts(amodule.check_mode)
|
||||||
amodule.exit_json(**self.result)
|
amodule.exit_json(**self.result)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -58,27 +58,40 @@ class decort_disk(DecortController):
|
|||||||
name=arg_amodule.params['name'] if "name" in arg_amodule.params else "",
|
name=arg_amodule.params['name'] if "name" in arg_amodule.params else "",
|
||||||
account_id=self.acc_id,
|
account_id=self.acc_id,
|
||||||
check_state=False,
|
check_state=False,
|
||||||
|
fail_if_not_found=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
if arg_amodule.params['place_with']:
|
if arg_amodule.params['place_with']:
|
||||||
image_id, image_facts = self.image_find(arg_amodule.params['place_with'], "", 0)
|
image_id, image_facts = self.image_find(arg_amodule.params['place_with'], "", 0)
|
||||||
arg_amodule.params['sep_id'] = image_facts['sepId']
|
arg_amodule.params['sep_id'] = image_facts['sep_id']
|
||||||
|
|
||||||
self.disk_id = validated_disk_id
|
self.disk_id = validated_disk_id
|
||||||
self.disk_info = validated_disk_facts
|
self.disk_info = validated_disk_facts
|
||||||
|
|
||||||
if self.disk_id:
|
if self.disk_id and self.disk_info.status not in [
|
||||||
self.acc_id = validated_disk_facts['accountId']
|
sdk_types.DiskStatus.DESTROYED,
|
||||||
|
sdk_types.DiskStatus.PURGED,
|
||||||
|
]:
|
||||||
|
self.acc_id = validated_disk_facts.account_id
|
||||||
self.check_amodule_args_for_change()
|
self.check_amodule_args_for_change()
|
||||||
else:
|
elif (
|
||||||
|
self.amodule.params['state'] == 'present'
|
||||||
|
and not arg_amodule.params['id']
|
||||||
|
):
|
||||||
self.check_amodule_args_for_create()
|
self.check_amodule_args_for_create()
|
||||||
|
|
||||||
def compare_iotune_params(self, new_iotune: dict, current_iotune: dict):
|
def compare_iotune_params(
|
||||||
|
self,
|
||||||
|
new_iotune: dict,
|
||||||
|
current_iotune: sdk_types.IOTuneAPIResultNM,
|
||||||
|
):
|
||||||
io_fields = sdk_types.IOTuneAPIResultNM.model_fields.keys()
|
io_fields = sdk_types.IOTuneAPIResultNM.model_fields.keys()
|
||||||
|
|
||||||
for field in io_fields:
|
for field in io_fields:
|
||||||
new_value = new_iotune.get(field)
|
new_value = new_iotune.get(field)
|
||||||
current_value = current_iotune.get(field)
|
if new_value is None:
|
||||||
|
continue
|
||||||
|
current_value = getattr(current_iotune, field, None)
|
||||||
|
|
||||||
if new_value != current_value:
|
if new_value != current_value:
|
||||||
return False
|
return False
|
||||||
@@ -115,7 +128,11 @@ class decort_disk(DecortController):
|
|||||||
)
|
)
|
||||||
#IO tune
|
#IO tune
|
||||||
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
|
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
|
||||||
self.limit_io(aparam_limit_io=aparam_limit_io)
|
if (
|
||||||
|
aparam_limit_io
|
||||||
|
and any(value is not None for value in aparam_limit_io.values())
|
||||||
|
):
|
||||||
|
self.limit_io(aparam_limit_io=aparam_limit_io)
|
||||||
#set share status
|
#set share status
|
||||||
if self.amodule.params['shareable']:
|
if self.amodule.params['shareable']:
|
||||||
self.sdk_checkmode(self.api.cloudapi.disks.share)(
|
self.sdk_checkmode(self.api.cloudapi.disks.share)(
|
||||||
@@ -133,13 +150,13 @@ class decort_disk(DecortController):
|
|||||||
#rename if id present
|
#rename if id present
|
||||||
if (
|
if (
|
||||||
self.amodule.params['name'] is not None
|
self.amodule.params['name'] is not None
|
||||||
and self.amodule.params['name'] != self.disk_info['name']
|
and self.amodule.params['name'] != self.disk_info.name
|
||||||
):
|
):
|
||||||
self.rename()
|
self.rename()
|
||||||
#resize
|
#resize
|
||||||
if (
|
if (
|
||||||
self.amodule.params['size'] is not None
|
self.amodule.params['size'] is not None
|
||||||
and self.amodule.params['size'] != self.disk_info['sizeMax']
|
and self.amodule.params['size'] != self.disk_info.size_max_gb
|
||||||
):
|
):
|
||||||
self.sdk_checkmode(self.api.cloudapi.disks.resize2)(
|
self.sdk_checkmode(self.api.cloudapi.disks.resize2)(
|
||||||
disk_id=self.disk_id,
|
disk_id=self.disk_id,
|
||||||
@@ -147,16 +164,19 @@ class decort_disk(DecortController):
|
|||||||
)
|
)
|
||||||
#IO TUNE
|
#IO TUNE
|
||||||
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
|
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
|
||||||
if aparam_limit_io:
|
if (
|
||||||
|
aparam_limit_io
|
||||||
|
and any(value is not None for value in aparam_limit_io.values())
|
||||||
|
):
|
||||||
if not self.compare_iotune_params(
|
if not self.compare_iotune_params(
|
||||||
new_iotune=aparam_limit_io,
|
new_iotune=aparam_limit_io,
|
||||||
current_iotune=self.disk_info['iotune'],
|
current_iotune=self.disk_info.io_tune,
|
||||||
):
|
):
|
||||||
self.limit_io(aparam_limit_io=aparam_limit_io)
|
self.limit_io(aparam_limit_io=aparam_limit_io)
|
||||||
|
|
||||||
#share check/update
|
#share check/update
|
||||||
#raise Exception(self.amodule.params['shareable'])
|
#raise Exception(self.amodule.params['shareable'])
|
||||||
if self.amodule.params['shareable'] != self.disk_info['shareable']:
|
if self.amodule.params['shareable'] != self.disk_info.shared:
|
||||||
if self.amodule.params['shareable']:
|
if self.amodule.params['shareable']:
|
||||||
self.sdk_checkmode(self.api.cloudapi.disks.share)(
|
self.sdk_checkmode(self.api.cloudapi.disks.share)(
|
||||||
disk_id=self.disk_id,
|
disk_id=self.disk_id,
|
||||||
@@ -169,7 +189,7 @@ class decort_disk(DecortController):
|
|||||||
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
||||||
if (
|
if (
|
||||||
aparam_storage_policy_id is not None
|
aparam_storage_policy_id is not None
|
||||||
and aparam_storage_policy_id != self.disk_info['storage_policy_id']
|
and aparam_storage_policy_id != self.disk_info.storage_policy_id
|
||||||
):
|
):
|
||||||
self.sdk_checkmode(self.api.ca.disks.change_disk_storage_policy)(
|
self.sdk_checkmode(self.api.ca.disks.change_disk_storage_policy)(
|
||||||
disk_id=self.disk_id,
|
disk_id=self.disk_id,
|
||||||
@@ -183,7 +203,7 @@ class decort_disk(DecortController):
|
|||||||
detach=self.amodule.params['force_detach'],
|
detach=self.amodule.params['force_detach'],
|
||||||
permanently=self.amodule.params['permanently'],
|
permanently=self.amodule.params['permanently'],
|
||||||
)
|
)
|
||||||
self.disk_id, self.disk_info = self._disk_get_by_id(self.disk_id)
|
self.disk_info = self._disk_get_by_id(disk_id=self.disk_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
def rename(self):
|
def rename(self):
|
||||||
@@ -199,7 +219,7 @@ class decort_disk(DecortController):
|
|||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
if self.disk_id:
|
if self.disk_id:
|
||||||
self.result['msg'] = ("No state change required for Disk ID {} because of its "
|
self.result['msg'] = ("No state change required for Disk ID {} because of its "
|
||||||
"current status '{}'.").format(self.disk_id, self.disk_info['status'])
|
"current status '{}'.").format(self.disk_id, self.disk_info.status)
|
||||||
else:
|
else:
|
||||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
self.result['msg'] = ("No state change to '{}' can be done for "
|
||||||
"non-existent Disk.").format(self.amodule.params['state'])
|
"non-existent Disk.").format(self.amodule.params['state'])
|
||||||
@@ -219,28 +239,7 @@ class decort_disk(DecortController):
|
|||||||
if check_mode or self.disk_info is None:
|
if check_mode or self.disk_info is None:
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
# remove io param with zero value
|
return self.disk_info.model_dump()
|
||||||
clean_io = [param for param in self.disk_info['iotune'] if self.disk_info['iotune'][param] == 0]
|
|
||||||
for key in clean_io: del self.disk_info['iotune'][key]
|
|
||||||
|
|
||||||
ret_dict['id'] = self.disk_info['id']
|
|
||||||
ret_dict['name'] = self.disk_info['name']
|
|
||||||
ret_dict['size'] = self.disk_info['sizeMax']
|
|
||||||
ret_dict['state'] = self.disk_info['status']
|
|
||||||
ret_dict['account_id'] = self.disk_info['accountId']
|
|
||||||
ret_dict['sep_id'] = self.disk_info['sepId']
|
|
||||||
ret_dict['pool'] = self.disk_info['pool']
|
|
||||||
ret_dict['computes'] = self.disk_info['computes']
|
|
||||||
ret_dict['gid'] = self.disk_info['gid']
|
|
||||||
ret_dict['iotune'] = self.disk_info['iotune']
|
|
||||||
ret_dict['size_available'] = self.disk_info['sizeAvailable']
|
|
||||||
ret_dict['size_used'] = self.disk_info['sizeUsed']
|
|
||||||
ret_dict['storage_policy_id'] = self.disk_info['storage_policy_id']
|
|
||||||
ret_dict['to_clean'] = self.disk_info['to_clean']
|
|
||||||
ret_dict['cache_mode'] = self.disk_info['cache']
|
|
||||||
ret_dict['blkdiscard'] = self.disk_info['blkdiscard']
|
|
||||||
|
|
||||||
return ret_dict
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amodule_init_args(self) -> dict:
|
def amodule_init_args(self) -> dict:
|
||||||
@@ -391,8 +390,8 @@ class decort_disk(DecortController):
|
|||||||
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
||||||
if (
|
if (
|
||||||
aparam_storage_policy_id is not None
|
aparam_storage_policy_id is not None
|
||||||
and aparam_storage_policy_id
|
and aparam_storage_policy_id
|
||||||
not in self.acc_info['storage_policy_ids']
|
not in self.acc_info.storage_policy_ids
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
@@ -431,24 +430,24 @@ class decort_disk(DecortController):
|
|||||||
amodule = self.amodule
|
amodule = self.amodule
|
||||||
if self.disk_id:
|
if self.disk_id:
|
||||||
#disk exist
|
#disk exist
|
||||||
if self.disk_info['status'] in ["MODELED", "CREATING"]:
|
if self.disk_info.status in [sdk_types.DiskStatus.MODELED, sdk_types.DiskStatus.CREATING]:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = ("No change can be done for existing Disk ID {} because of its current "
|
self.result['msg'] = ("No change can be done for existing Disk ID {} because of its current "
|
||||||
"status '{}'").format(self.disk_id, self.disk_info['status'])
|
"status '{}'").format(self.disk_id, self.disk_info.status)
|
||||||
# "ASSIGNED","CREATED","DELETED","PURGED", "DESTROYED"
|
# "ASSIGNED","CREATED","DELETED","PURGED", "DESTROYED"
|
||||||
elif self.disk_info['status'] in ["ASSIGNED","CREATED"]:
|
elif self.disk_info.status in [sdk_types.DiskStatus.CREATED, sdk_types.DiskStatus.ASSIGNED]:
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
self.delete()
|
self.delete()
|
||||||
elif amodule.params['state'] == 'present':
|
elif amodule.params['state'] == 'present':
|
||||||
self.action()
|
self.action()
|
||||||
elif self.disk_info['status'] in ["PURGED", "DESTROYED"]:
|
elif self.disk_info.status in [sdk_types.DiskStatus.PURGED, sdk_types.DiskStatus.DESTROYED]:
|
||||||
#re-provision disk
|
#re-provision disk
|
||||||
if amodule.params['state'] in ('present'):
|
if amodule.params['state'] in ('present'):
|
||||||
self.create()
|
self.create()
|
||||||
else:
|
else:
|
||||||
self.nop()
|
self.nop()
|
||||||
elif self.disk_info['status'] == "DELETED":
|
elif self.disk_info.status == sdk_types.DiskStatus.DELETED:
|
||||||
if amodule.params['state'] in ('present'):
|
if amodule.params['state'] in ('present'):
|
||||||
self.action(restore=True)
|
self.action(restore=True)
|
||||||
elif (amodule.params['state'] == 'absent' and
|
elif (amodule.params['state'] == 'absent' and
|
||||||
@@ -459,8 +458,24 @@ class decort_disk(DecortController):
|
|||||||
else:
|
else:
|
||||||
# preexisting Disk was not found
|
# preexisting Disk was not found
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
self.nop()
|
self.exit()
|
||||||
else:
|
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
amodule.params['state'] == 'present'
|
||||||
|
or amodule.params['state'] is None
|
||||||
|
)
|
||||||
|
and amodule.params['id']
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
f'Disk with ID {amodule.params['id']} not found.'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
if (
|
||||||
|
amodule.params['state'] == 'present'
|
||||||
|
and not amodule.params['id']
|
||||||
|
):
|
||||||
self.create()
|
self.create()
|
||||||
|
|
||||||
if self.result['failed']:
|
if self.result['failed']:
|
||||||
|
|||||||
@@ -59,9 +59,11 @@ class DecortDiskList(DecortController):
|
|||||||
storage_policy_id=dict(
|
storage_policy_id=dict(
|
||||||
type='int',
|
type='int',
|
||||||
),
|
),
|
||||||
type=dict(
|
rg_id=dict(
|
||||||
type='str',
|
type='int',
|
||||||
choices=sdk_types.DiskType._member_names_,
|
),
|
||||||
|
vm_id=dict(
|
||||||
|
type='int',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -75,7 +77,7 @@ class DecortDiskList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -108,7 +110,6 @@ class DecortDiskList(DecortController):
|
|||||||
def get_info(self):
|
def get_info(self):
|
||||||
aparam_filter: dict[str, Any] = self.aparams['filter']
|
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||||
aparam_status: str | None = aparam_filter['status']
|
aparam_status: str | None = aparam_filter['status']
|
||||||
aparam_type: str | None = aparam_filter['type']
|
|
||||||
|
|
||||||
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||||
|
|
||||||
@@ -137,10 +138,8 @@ class DecortDiskList(DecortController):
|
|||||||
if aparam_status else None
|
if aparam_status else None
|
||||||
),
|
),
|
||||||
storage_policy_id=aparam_filter['storage_policy_id'],
|
storage_policy_id=aparam_filter['storage_policy_id'],
|
||||||
type=(
|
rg_id=aparam_filter['rg_id'],
|
||||||
sdk_types.DiskType[aparam_type]
|
vm_id=aparam_filter['vm_id'],
|
||||||
if aparam_type else None
|
|
||||||
),
|
|
||||||
page_number=aparam_pagination['number'],
|
page_number=aparam_pagination['number'],
|
||||||
page_size=aparam_pagination['size'],
|
page_size=aparam_pagination['size'],
|
||||||
sort_by=sort_by,
|
sort_by=sort_by,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class DecortFlipGroupList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ class decort_group(DecortController):
|
|||||||
validated_bservice_id = None
|
validated_bservice_id = None
|
||||||
#find and validate B-Service
|
#find and validate B-Service
|
||||||
|
|
||||||
validated_bservice_id, bservice_info = self.bservice_get_by_id(arg_amodule.params['bservice_id'])
|
validated_bservice_id, self._bservice_info = self.bservice_find(
|
||||||
|
account_id=None,
|
||||||
|
rg_id=None,
|
||||||
|
bservice_id=arg_amodule.params['bservice_id'],
|
||||||
|
fail_if_not_found=False,
|
||||||
|
)
|
||||||
if not validated_bservice_id:
|
if not validated_bservice_id:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
@@ -29,15 +34,14 @@ class decort_group(DecortController):
|
|||||||
self.amodule.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
#find group
|
#find group
|
||||||
self.bservice_id = validated_bservice_id
|
self.bservice_id = validated_bservice_id
|
||||||
self.bservice_info = bservice_info
|
|
||||||
self.group_id,self.group_info = self.group_find(
|
self.group_id,self.group_info = self.group_find(
|
||||||
bs_id=validated_bservice_id,
|
bs_id=validated_bservice_id,
|
||||||
bs_info=bservice_info,
|
bs_info=self.bservice_info,
|
||||||
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.acc_id = self.bservice_info['account_id']
|
||||||
self.rg_id = self.bservice_info['rgId']
|
self.rg_id = self.bservice_info['rg_id']
|
||||||
|
|
||||||
if self.group_id:
|
if self.group_id:
|
||||||
self.group_should_exist = True
|
self.group_should_exist = True
|
||||||
@@ -88,17 +92,19 @@ class decort_group(DecortController):
|
|||||||
warning=True,
|
warning=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.group_id=self.group_provision(
|
aparam_networks = self.amodule.params['networks'] or []
|
||||||
bs_id=self.bservice_id,
|
self.group_id = self.sdk_checkmode(self.api.cloudapi.bservice.group_add)(
|
||||||
arg_name=self.amodule.params['name'],
|
bservice_id=self.bservice_id,
|
||||||
arg_count=self.amodule.params['count'],
|
name=self.amodule.params['name'],
|
||||||
arg_cpu=self.amodule.params['cpu'],
|
vm_count=self.amodule.params['count'],
|
||||||
arg_ram=self.amodule.params['ram'],
|
cpu_count=self.amodule.params['cpu'],
|
||||||
arg_boot_disk=self.amodule.params['boot_disk'],
|
ram_size_mb=self.amodule.params['ram'],
|
||||||
arg_image_id=self.amodule.params['image_id'],
|
boot_disk_size_gb=self.amodule.params['boot_disk'],
|
||||||
arg_role=self.amodule.params['role'],
|
image_id=self.amodule.params['image_id'],
|
||||||
arg_network=self.amodule.params['networks'],
|
role=self.amodule.params['role'],
|
||||||
arg_timeout=self.amodule.params['timeoutStart'],
|
vins_ids=[n['id'] for n in aparam_networks if n['type'] == 'VINS'],
|
||||||
|
extnet_ids=[n['id'] for n in aparam_networks if n['type'] == 'EXTNET'],
|
||||||
|
start_timeout=self.amodule.params['timeoutStart'],
|
||||||
chipset=chipset,
|
chipset=chipset,
|
||||||
storage_policy_id=self.aparams['storage_policy_id'],
|
storage_policy_id=self.aparams['storage_policy_id'],
|
||||||
)
|
)
|
||||||
@@ -146,14 +152,15 @@ class decort_group(DecortController):
|
|||||||
aparam_value = self.aparams[aparam_name]
|
aparam_value = self.aparams[aparam_name]
|
||||||
group_info_value = self.group_info[info_key]
|
group_info_value = self.group_info[info_key]
|
||||||
if aparam_value != None and aparam_value != group_info_value:
|
if aparam_value != None and aparam_value != group_info_value:
|
||||||
self.group_update(
|
self.sdk_checkmode(self.api.cloudapi.bservice.group_update)(
|
||||||
bs_id=self.bservice_id,
|
bservice_id=self.bservice_id,
|
||||||
gr_dict=self.group_info,
|
bsgroup_id=self.group_info['id'],
|
||||||
arg_cpu=self.aparams['cpu'],
|
force=True,
|
||||||
arg_disk=self.aparams['boot_disk'],
|
cpu_count=self.aparams['cpu'],
|
||||||
arg_name=self.aparams['name'],
|
boot_disk_size_gb=self.aparams['boot_disk'],
|
||||||
arg_role=self.aparams['role'],
|
name=self.aparams['name'],
|
||||||
arg_ram=self.aparams['ram'],
|
ram_size_mb=self.aparams['ram'],
|
||||||
|
role=self.aparams['role'],
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -166,9 +173,9 @@ class decort_group(DecortController):
|
|||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
|
|
||||||
self.group_delete(
|
self.sdk_checkmode(self.api.cloudapi.bservice.group_remove)(
|
||||||
self.bservice_id,
|
bservice_id=self.bservice_id,
|
||||||
self.group_id
|
bsgroup_id=self.group_id,
|
||||||
)
|
)
|
||||||
self.group_should_exist = False
|
self.group_should_exist = False
|
||||||
|
|
||||||
@@ -356,7 +363,7 @@ class decort_group(DecortController):
|
|||||||
else:
|
else:
|
||||||
if (
|
if (
|
||||||
aparam_storage_policy_id
|
aparam_storage_policy_id
|
||||||
not in self.rg_info['storage_policy_ids']
|
not in self.rg_info.storage_policy_ids
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
@@ -366,7 +373,7 @@ class decort_group(DecortController):
|
|||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
aparam_storage_policy_id
|
aparam_storage_policy_id
|
||||||
not in self.acc_info['storage_policy_ids']
|
not in self.acc_info.storage_policy_ids
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ class decort_image(DecortController):
|
|||||||
super(decort_image, self).__init__(AnsibleModule(**self.amodule_init_args))
|
super(decort_image, self).__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
amodule = self.amodule
|
amodule = self.amodule
|
||||||
|
|
||||||
self.validated_image_id = 0
|
self.validated_image_id: int = 0
|
||||||
self.validated_virt_image_id = 0
|
self.validated_virt_image_id: int = 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.image_info: dict
|
self.image_info: dict
|
||||||
@@ -46,9 +46,9 @@ class decort_image(DecortController):
|
|||||||
)
|
)
|
||||||
if self.virt_image_info:
|
if self.virt_image_info:
|
||||||
_, linked_image_info = self._image_get_by_id(
|
_, linked_image_info = self._image_get_by_id(
|
||||||
image_id=self.virt_image_info['linkTo']
|
image_id=self.virt_image_info['link_to']
|
||||||
)
|
)
|
||||||
self.acc_id = linked_image_info['accountId']
|
self.acc_id = linked_image_info['account_id']
|
||||||
if (
|
if (
|
||||||
self.aparams['virt_name'] is not None
|
self.aparams['virt_name'] is not None
|
||||||
and self.aparams['virt_name']
|
and self.aparams['virt_name']
|
||||||
@@ -61,10 +61,10 @@ class decort_image(DecortController):
|
|||||||
or self.aparams['image_name'] is not None
|
or self.aparams['image_name'] is not None
|
||||||
):
|
):
|
||||||
self.validated_image_id, self.image_info = (
|
self.validated_image_id, self.image_info = (
|
||||||
self.decort_image_find(amodule)
|
self.decort_image_find(amodule, fail_if_not_found=False)
|
||||||
)
|
)
|
||||||
if self.image_info:
|
if self.image_info:
|
||||||
self.acc_id = self.image_info['accountId']
|
self.acc_id = self.image_info['account_id']
|
||||||
if (
|
if (
|
||||||
amodule.params['image_name']
|
amodule.params['image_name']
|
||||||
and amodule.params['image_name'] != self.image_info['name']
|
and amodule.params['image_name'] != self.image_info['name']
|
||||||
@@ -72,6 +72,23 @@ class decort_image(DecortController):
|
|||||||
decort_image.decort_image_rename(self,amodule)
|
decort_image.decort_image_rename(self,amodule)
|
||||||
self.result['msg'] = ("Image renamed successfully")
|
self.result['msg'] = ("Image renamed successfully")
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.validated_image_id == 0
|
||||||
|
and self.aparams['image_id']
|
||||||
|
and not self.validated_virt_image_id
|
||||||
|
and not self.aparams['virt_name']
|
||||||
|
):
|
||||||
|
if self.aparams['state'] == 'absent':
|
||||||
|
self.exit()
|
||||||
|
else:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='image',
|
||||||
|
id=self.aparams['image_id'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
if self.validated_image_id:
|
if self.validated_image_id:
|
||||||
self.check_amodule_args_for_change()
|
self.check_amodule_args_for_change()
|
||||||
elif self.validated_virt_image_id:
|
elif self.validated_virt_image_id:
|
||||||
@@ -81,12 +98,13 @@ class decort_image(DecortController):
|
|||||||
else:
|
else:
|
||||||
self.check_amodule_args_for_create_image()
|
self.check_amodule_args_for_create_image()
|
||||||
|
|
||||||
def decort_image_find(self, amodule):
|
def decort_image_find(self, amodule, fail_if_not_found=True):
|
||||||
# function that finds the OS image
|
# function that finds the OS image
|
||||||
image_id, image_facts = self.image_find(image_id=amodule.params['image_id'], image_name=self.validated_image_name,
|
image_id, image_facts = self.image_find(image_id=amodule.params['image_id'], image_name=self.validated_image_name,
|
||||||
account_id=self.validated_account_id, rg_id=0,
|
account_id=self.validated_account_id, rg_id=0,
|
||||||
sepid=amodule.params['sep_id'],
|
sepid=amodule.params['sep_id'],
|
||||||
pool=amodule.params['pool'])
|
pool=amodule.params['pool'],
|
||||||
|
fail_if_not_found=fail_if_not_found,)
|
||||||
return image_id, image_facts
|
return image_id, image_facts
|
||||||
|
|
||||||
def decort_virt_image_find(self, amodule):
|
def decort_virt_image_find(self, amodule):
|
||||||
@@ -150,28 +168,30 @@ class decort_image(DecortController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# function that creates OS image
|
# function that creates OS image
|
||||||
image_facts = self.image_create(
|
self.sdk_checkmode(self.api.ca.image.create)(
|
||||||
img_name=self.validated_image_name,
|
name=self.validated_image_name,
|
||||||
url=amodule.params['url'],
|
download_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'],
|
os_user_name=amodule.params['image_username'],
|
||||||
password=amodule.params['image_password'],
|
os_password=amodule.params['image_password'],
|
||||||
account_id=self.validated_account_id,
|
account_id=self.validated_account_id,
|
||||||
usernameDL=amodule.params['usernameDL'],
|
download_user_name=amodule.params['usernameDL'],
|
||||||
passwordDL=amodule.params['passwordDL'],
|
download_password=amodule.params['passwordDL'],
|
||||||
sepId=amodule.params['sepId'],
|
sep_id=amodule.params['sepId'],
|
||||||
poolName=amodule.params['poolName'],
|
sep_pool_name=amodule.params['poolName'],
|
||||||
network_interface_naming=network_interface_naming,
|
network_interface_naming=network_interface_naming,
|
||||||
storage_policy_id=amodule.params['storage_policy_id'],
|
storage_policy_id=amodule.params['storage_policy_id'],
|
||||||
)
|
)
|
||||||
self.result['changed'] = True
|
return
|
||||||
return image_facts
|
|
||||||
|
|
||||||
def decort_virt_image_link(self,amodule):
|
def decort_virt_image_link(self,amodule):
|
||||||
# function that links an OS image to a virtual one
|
# function that links an OS image to a virtual one
|
||||||
self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.target_image_id)
|
self.sdk_checkmode(self.api.ca.image.link)(
|
||||||
|
virtual_image_id=self.validated_virt_image_id,
|
||||||
|
target_image_id=self.target_image_id,
|
||||||
|
)
|
||||||
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
|
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
|
||||||
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
||||||
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.target_image_id,
|
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.target_image_id,
|
||||||
@@ -180,32 +200,41 @@ class decort_image(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.sdk_checkmode(self.api.ca.image.delete)(
|
||||||
|
image_id=amodule.image_id_delete
|
||||||
|
)
|
||||||
_, image_facts = decort_image._image_get_by_id(self, amodule.image_id_delete)
|
_, image_facts = decort_image._image_get_by_id(self, amodule.image_id_delete)
|
||||||
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
||||||
return
|
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
|
||||||
image_facts = self.virt_image_create(
|
self.sdk_checkmode(self.api.ca.image.create_virtual)(
|
||||||
name=amodule.params['virt_name'],
|
name=amodule.params['virt_name'],
|
||||||
target_id=self.target_image_id,
|
target_image_id=self.target_image_id,
|
||||||
account_id=self.aparams['account_id'],
|
account_id=self.aparams['account_id'],
|
||||||
)
|
)
|
||||||
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
|
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
|
||||||
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
||||||
return image_id, image_facts
|
return image_id, image_facts
|
||||||
|
|
||||||
|
@DecortController.handle_sdk_exceptions
|
||||||
def decort_image_rename(self,amodule):
|
def decort_image_rename(self,amodule):
|
||||||
# image renaming function
|
# image renaming function
|
||||||
image_facts = self.image_rename(imageId=self.validated_image_id, name=amodule.params['image_name'])
|
self.sdk_checkmode(self.api.ca.image.rename)(
|
||||||
|
image_id=self.validated_image_id,
|
||||||
|
name=amodule.params['image_name'],
|
||||||
|
)
|
||||||
self.result['msg'] = ("Image renamed successfully")
|
self.result['msg'] = ("Image renamed successfully")
|
||||||
image_id, image_facts = decort_image.decort_image_find(self, amodule)
|
image_id, image_facts = decort_image.decort_image_find(self, amodule)
|
||||||
return image_id, image_facts
|
return image_id, image_facts
|
||||||
|
|
||||||
|
@DecortController.handle_sdk_exceptions
|
||||||
def decort_virt_image_rename(self, amodule):
|
def decort_virt_image_rename(self, amodule):
|
||||||
image_facts = self.image_rename(imageId=self.validated_virt_image_id,
|
self.sdk_checkmode(self.api.ca.image.rename)(
|
||||||
name=amodule.params['virt_name'])
|
image_id=self.validated_virt_image_id,
|
||||||
|
name=amodule.params['virt_name'],
|
||||||
|
)
|
||||||
self.result['msg'] = ("Virtual image renamed successfully")
|
self.result['msg'] = ("Virtual image renamed successfully")
|
||||||
image_id, image_facts = self.decort_virt_image_find(amodule)
|
image_id, image_facts = self.decort_virt_image_find(amodule)
|
||||||
return image_id, image_facts
|
return image_id, image_facts
|
||||||
@@ -241,14 +270,14 @@ class decort_image(DecortController):
|
|||||||
|
|
||||||
ret_dict['id'] = arg_image_facts['id']
|
ret_dict['id'] = arg_image_facts['id']
|
||||||
ret_dict['name'] = arg_image_facts['name']
|
ret_dict['name'] = arg_image_facts['name']
|
||||||
ret_dict['size'] = arg_image_facts['size']
|
ret_dict['size'] = arg_image_facts['size_gb']
|
||||||
# ret_dict['arch'] = arg_image_facts['architecture']
|
# ret_dict['arch'] = arg_image_facts['architecture']
|
||||||
ret_dict['sep_id'] = arg_image_facts['sepId']
|
ret_dict['sep_id'] = arg_image_facts['sep_id']
|
||||||
ret_dict['pool'] = arg_image_facts['pool']
|
ret_dict['pool'] = arg_image_facts['sep_pool_name']
|
||||||
ret_dict['state'] = arg_image_facts['status']
|
ret_dict['state'] = arg_image_facts['status']
|
||||||
ret_dict['linkto'] = arg_image_facts['linkTo']
|
ret_dict['linkto'] = arg_image_facts['link_to']
|
||||||
ret_dict['accountId'] = arg_image_facts['accountId']
|
ret_dict['accountId'] = arg_image_facts['account_id']
|
||||||
ret_dict['boot_mode'] = arg_image_facts['bootType']
|
ret_dict['boot_mode'] = arg_image_facts['boot_mode']
|
||||||
|
|
||||||
ret_dict['boot_loader_type'] = ''
|
ret_dict['boot_loader_type'] = ''
|
||||||
match arg_image_facts['type']:
|
match arg_image_facts['type']:
|
||||||
@@ -259,11 +288,13 @@ class decort_image(DecortController):
|
|||||||
ret_dict['boot_loader_type'] = boot_loader_type
|
ret_dict['boot_loader_type'] = boot_loader_type
|
||||||
|
|
||||||
ret_dict['network_interface_naming'] = arg_image_facts[
|
ret_dict['network_interface_naming'] = arg_image_facts[
|
||||||
'networkInterfaceNaming'
|
'network_interface_naming'
|
||||||
]
|
]
|
||||||
ret_dict['hot_resize'] = arg_image_facts['hotResize']
|
ret_dict['hot_resize'] = arg_image_facts['hot_resize']
|
||||||
ret_dict['storage_policy_id'] = arg_image_facts['storage_policy_id']
|
ret_dict['storage_policy_id'] = arg_image_facts['storage_policy_id']
|
||||||
ret_dict['to_clean'] = arg_image_facts['to_clean']
|
ret_dict['to_clean'] = arg_image_facts['to_clean']
|
||||||
|
ret_dict['independent'] = arg_image_facts['independent']
|
||||||
|
ret_dict['links_to'] = arg_image_facts['links_to']
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -372,7 +403,7 @@ class decort_image(DecortController):
|
|||||||
if (
|
if (
|
||||||
aparam_storage_policy_id is not None
|
aparam_storage_policy_id is not None
|
||||||
and aparam_storage_policy_id
|
and aparam_storage_policy_id
|
||||||
not in self.acc_info['storage_policy_ids']
|
not in self.acc_info.storage_policy_ids
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
@@ -426,7 +457,7 @@ class decort_image(DecortController):
|
|||||||
)
|
)
|
||||||
elif (
|
elif (
|
||||||
aparam_storage_policy_id
|
aparam_storage_policy_id
|
||||||
not in self.acc_info['storage_policy_ids']
|
not in self.acc_info.storage_policy_ids
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
@@ -491,7 +522,7 @@ class decort_image(DecortController):
|
|||||||
and amodule.params['storage_policy_id']
|
and amodule.params['storage_policy_id']
|
||||||
!= image_facts['storage_policy_id']
|
!= image_facts['storage_policy_id']
|
||||||
):
|
):
|
||||||
self.image_change_storage_policy(
|
self.sdk_checkmode(self.api.ca.image.change_storage_policy)(
|
||||||
image_id=self.validated_virt_image_id,
|
image_id=self.validated_virt_image_id,
|
||||||
storage_policy_id=amodule.params['storage_policy_id'],
|
storage_policy_id=amodule.params['storage_policy_id'],
|
||||||
)
|
)
|
||||||
@@ -499,7 +530,7 @@ class decort_image(DecortController):
|
|||||||
if amodule.params['state'] == "absent" and self.validated_virt_image_id:
|
if amodule.params['state'] == "absent" and self.validated_virt_image_id:
|
||||||
amodule.image_id_delete = self.validated_virt_image_id
|
amodule.image_id_delete = self.validated_virt_image_id
|
||||||
image_id, image_facts = self.decort_virt_image_find(amodule)
|
image_id, image_facts = self.decort_virt_image_find(amodule)
|
||||||
if image_facts['status'] != 'PURGED':
|
if image_facts['status'] != sdk_types.ImageStatus.PURGED:
|
||||||
self.decort_image_delete(amodule)
|
self.decort_image_delete(amodule)
|
||||||
|
|
||||||
elif amodule.params['image_name'] or amodule.params['image_id']:
|
elif amodule.params['image_name'] or amodule.params['image_id']:
|
||||||
@@ -519,7 +550,10 @@ class decort_image(DecortController):
|
|||||||
elif amodule.params['state'] == "absent" and self.validated_image_id:
|
elif amodule.params['state'] == "absent" and self.validated_image_id:
|
||||||
amodule.image_id_delete = self.validated_image_id
|
amodule.image_id_delete = self.validated_image_id
|
||||||
image_id, image_facts = self.decort_image_find(amodule)
|
image_id, image_facts = self.decort_image_find(amodule)
|
||||||
if image_facts['status'] != 'DESTROYED':
|
if image_facts['status'] not in [
|
||||||
|
sdk_types.ImageStatus.DESTROYED,
|
||||||
|
sdk_types.ImageStatus.PURGED,
|
||||||
|
]:
|
||||||
self.decort_image_delete(amodule)
|
self.decort_image_delete(amodule)
|
||||||
|
|
||||||
if self.validated_image_id:
|
if self.validated_image_id:
|
||||||
@@ -528,7 +562,7 @@ class decort_image(DecortController):
|
|||||||
and amodule.params['storage_policy_id']
|
and amodule.params['storage_policy_id']
|
||||||
!= image_facts['storage_policy_id']
|
!= image_facts['storage_policy_id']
|
||||||
):
|
):
|
||||||
self.image_change_storage_policy(
|
self.sdk_checkmode(self.api.ca.image.change_storage_policy)(
|
||||||
image_id=self.validated_image_id,
|
image_id=self.validated_image_id,
|
||||||
storage_policy_id=amodule.params['storage_policy_id'],
|
storage_policy_id=amodule.params['storage_policy_id'],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ class DecortImageList(DecortController):
|
|||||||
choices=sdk_types.ImageStatus._member_names_,
|
choices=sdk_types.ImageStatus._member_names_,
|
||||||
),
|
),
|
||||||
type=dict(
|
type=dict(
|
||||||
type='str',
|
type='list',
|
||||||
|
elements='str',
|
||||||
choices=sdk_types.ImageType._member_names_,
|
choices=sdk_types.ImageType._member_names_,
|
||||||
),
|
),
|
||||||
storage_policy_id=dict(
|
storage_policy_id=dict(
|
||||||
@@ -145,7 +146,7 @@ class DecortImageList(DecortController):
|
|||||||
),
|
),
|
||||||
storage_policy_id=aparam_filter['storage_policy_id'],
|
storage_policy_id=aparam_filter['storage_policy_id'],
|
||||||
type=(
|
type=(
|
||||||
sdk_types.ImageType[aparam_type]
|
[sdk_types.ImageType[type_] for type_ in aparam_type]
|
||||||
if aparam_type else None
|
if aparam_type else None
|
||||||
),
|
),
|
||||||
page_number=aparam_pagination['number'],
|
page_number=aparam_pagination['number'],
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class decort_k8s(DecortController):
|
|||||||
|
|
||||||
validated_acc_id = 0
|
validated_acc_id = 0
|
||||||
validated_rg_id = 0
|
validated_rg_id = 0
|
||||||
validated_rg_facts = None
|
validated_rg_model = None
|
||||||
validated_k8ci_id = 0
|
validated_k8ci_id = 0
|
||||||
self.k8s_should_exist = False
|
self.k8s_should_exist = False
|
||||||
self.is_k8s_stopped_or_will_be_stopped: None | bool = None
|
self.is_k8s_stopped_or_will_be_stopped: None | bool = None
|
||||||
@@ -55,12 +55,12 @@ class decort_k8s(DecortController):
|
|||||||
self.amodule.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
# fail the module -> exit
|
# fail the module -> exit
|
||||||
# now validate RG
|
# now validate RG
|
||||||
validated_rg_id, validated_rg_facts = self.rg_find(
|
validated_rg_id, validated_rg_model = self.rg_find(
|
||||||
arg_account_id=validated_acc_id,
|
arg_account_id=validated_acc_id,
|
||||||
arg_rg_id=arg_amodule.params['rg_id'],
|
arg_rg_id=arg_amodule.params['rg_id'],
|
||||||
arg_rg_name=arg_amodule.params['rg_name']
|
arg_rg_name=arg_amodule.params['rg_name']
|
||||||
)
|
)
|
||||||
if not validated_rg_id:
|
if not validated_rg_id or not validated_rg_model:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||||
@@ -70,19 +70,21 @@ class decort_k8s(DecortController):
|
|||||||
|
|
||||||
self.rg_id = validated_rg_id
|
self.rg_id = validated_rg_id
|
||||||
arg_amodule.params['rg_id'] = validated_rg_id
|
arg_amodule.params['rg_id'] = validated_rg_id
|
||||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
arg_amodule.params['rg_name'] = validated_rg_model.name
|
||||||
self.acc_id = validated_rg_facts['accountId']
|
self.acc_id = validated_rg_model.account_id
|
||||||
|
|
||||||
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=arg_amodule.params['id'],
|
self.k8s_id, self._k8s_info = self.k8s_find(
|
||||||
k8s_name=arg_amodule.params['name'],
|
k8s_id=arg_amodule.params['id'],
|
||||||
rg_id=validated_rg_id,
|
k8s_name=arg_amodule.params['name'],
|
||||||
check_state=False)
|
rg_id=validated_rg_id,
|
||||||
|
check_state=False,
|
||||||
|
)
|
||||||
|
|
||||||
if self.k8s_id and self.k8s_info['status'] != 'DESTROYED':
|
if self.k8s_id and self.k8s_info['status'] != 'DESTROYED':
|
||||||
self.k8s_should_exist = True
|
self.k8s_should_exist = True
|
||||||
self.acc_id = self.k8s_info['accountId']
|
self.acc_id = self.k8s_info['account_id']
|
||||||
self.check_amodule_args_for_change()
|
self.check_amodule_args_for_change()
|
||||||
else:
|
elif arg_amodule.params['state'] != 'absent':
|
||||||
self.check_amodule_args_for_create()
|
self.check_amodule_args_for_create()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -96,32 +98,16 @@ class decort_k8s(DecortController):
|
|||||||
config=None,
|
config=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.amodule.params['getConfig'] and self.k8s_info['techStatus'] == "STARTED":
|
|
||||||
ret_dict['config'] = self.k8s_getConfig()
|
|
||||||
|
|
||||||
if check_mode:
|
if check_mode:
|
||||||
# in check mode return immediately with the default values
|
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
#if self.k8s_facts is None:
|
self.k8s_info['vins_id'] = self.k8s_vins_id
|
||||||
# #if void facts provided - change state value to ABSENT and return
|
self.k8s_info['config'] = None
|
||||||
# ret_dict['state'] = "ABSENT"
|
if self.amodule.params['getConfig'] and self.k8s_info['tech_status'] == "STARTED":
|
||||||
# return ret_dict
|
self.k8s_info['config'] = self.api.cloudapi.k8s.get_config(
|
||||||
|
k8s_id=self.k8s_id,
|
||||||
ret_dict['id'] = self.k8s_info['id']
|
)
|
||||||
ret_dict['name'] = self.k8s_info['name']
|
return self.k8s_info
|
||||||
ret_dict['techStatus'] = self.k8s_info['techStatus']
|
|
||||||
ret_dict['state'] = self.k8s_info['status']
|
|
||||||
ret_dict['rg_id'] = self.k8s_info['rgId']
|
|
||||||
ret_dict['vins_id'] = self.k8s_vins_id
|
|
||||||
ret_dict['account_id'] = self.acc_id
|
|
||||||
ret_dict['k8s_Masters'] = self.k8s_info['k8sGroups']['masters']
|
|
||||||
ret_dict['k8s_Workers'] = self.k8s_info['k8sGroups']['workers']
|
|
||||||
ret_dict['lb_id'] = self.k8s_info['lbId']
|
|
||||||
ret_dict['description'] = self.k8s_info['desc']
|
|
||||||
ret_dict['zone_id'] = self.k8s_info['zoneId']
|
|
||||||
|
|
||||||
return ret_dict
|
|
||||||
|
|
||||||
def nop(self):
|
def nop(self):
|
||||||
"""No operation (NOP) handler for k8s cluster management by decort_k8s module.
|
"""No operation (NOP) handler for k8s cluster management by decort_k8s module.
|
||||||
@@ -204,10 +190,12 @@ class decort_k8s(DecortController):
|
|||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.amodule.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
|
|
||||||
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=k8s_id,
|
self.k8s_id, self._k8s_info = self.k8s_find(
|
||||||
k8s_name=self.amodule.params['name'],
|
k8s_id=k8s_id,
|
||||||
rg_id=self.rg_id,
|
k8s_name=self.amodule.params['name'],
|
||||||
check_state=False)
|
rg_id=self.rg_id,
|
||||||
|
check_state=False,
|
||||||
|
)
|
||||||
|
|
||||||
if self.k8s_id:
|
if self.k8s_id:
|
||||||
self.k8s_should_exist = True
|
self.k8s_should_exist = True
|
||||||
@@ -215,21 +203,26 @@ class decort_k8s(DecortController):
|
|||||||
self.k8s_workers_modify(
|
self.k8s_workers_modify(
|
||||||
arg_k8swg=self.k8s_info,
|
arg_k8swg=self.k8s_info,
|
||||||
arg_modwg=target_wgs,
|
arg_modwg=target_wgs,
|
||||||
storage_policy_id=self.aparams['storage_policy_id'],
|
master_node_storage_policy_id=(
|
||||||
|
self.aparams['storage_policy_id']
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
self.k8s_delete(self.k8s_id,self.amodule.params['permanent'])
|
self.sdk_checkmode(self.api.ca.k8s.delete)(
|
||||||
|
k8s_id=self.k8s_id,
|
||||||
|
permanently=self.amodule.params['permanent'],
|
||||||
|
)
|
||||||
self.k8s_info['status'] = 'DELETED'
|
self.k8s_info['status'] = 'DELETED'
|
||||||
self.k8s_should_exist = False
|
self.k8s_should_exist = False
|
||||||
return
|
return
|
||||||
|
|
||||||
def action(self, disared_state, preupdate: bool = False):
|
def action(self, disared_state, preupdate: bool = False):
|
||||||
if self.amodule.params['master_chipset'] is not None:
|
if self.amodule.params['master_chipset'] is not None:
|
||||||
for master_node in self.k8s_info['k8sGroups']['masters'][
|
for master_node in self.k8s_info['node_groups']['master'][
|
||||||
'detailedInfo'
|
'vms'
|
||||||
]:
|
]:
|
||||||
_, master_node_info, _ = self._compute_get_by_id(
|
_, master_node_info, _ = self._compute_get_by_id(
|
||||||
comp_id=master_node['id']
|
comp_id=master_node['id']
|
||||||
@@ -245,17 +238,27 @@ class decort_k8s(DecortController):
|
|||||||
self.exit(fail=True)
|
self.exit(fail=True)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self.aparams['name'] is not None
|
(
|
||||||
and self.aparams['name'] != self.k8s_info['name']
|
self.aparams['name'] is not None
|
||||||
|
and self.aparams['name'] != self.k8s_info['name']
|
||||||
|
)
|
||||||
|
or (
|
||||||
|
self.aparams['description'] is not None
|
||||||
|
and self.aparams['description'] != self.k8s_info['description']
|
||||||
|
)
|
||||||
):
|
):
|
||||||
self.k8s_update(id=self.k8s_id, name=self.aparams['name'])
|
self.sdk_checkmode(self.api.ca.k8s.update)(
|
||||||
|
k8s_id=self.k8s_id,
|
||||||
|
description=self.aparams['description'],
|
||||||
|
name=self.aparams['name'],
|
||||||
|
)
|
||||||
|
|
||||||
if preupdate:
|
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)
|
||||||
#k8s state
|
#k8s state
|
||||||
self.k8s_state(self.k8s_info, disared_state)
|
self.k8s_state(self.k8s_info, disared_state)
|
||||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||||
#check groups and modify if needed
|
#check groups and modify if needed
|
||||||
if self.aparams['workers'] is not None:
|
if self.aparams['workers'] is not None:
|
||||||
self.k8s_workers_modify(
|
self.k8s_workers_modify(
|
||||||
@@ -264,14 +267,17 @@ class decort_k8s(DecortController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
aparam_zone_id = self.aparams['zone_id']
|
aparam_zone_id = self.aparams['zone_id']
|
||||||
if aparam_zone_id is not None and aparam_zone_id != self.k8s_info['zoneId']:
|
if (
|
||||||
self.k8s_migrate_to_zone(
|
aparam_zone_id is not None
|
||||||
|
and aparam_zone_id != self.k8s_info['zone_id']
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.k8s.migrate_to_zone)(
|
||||||
k8s_id=self.k8s_id,
|
k8s_id=self.k8s_id,
|
||||||
zone_id=aparam_zone_id,
|
zone_id=aparam_zone_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.result['changed'] == True:
|
if self.result['changed'] == True:
|
||||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||||
#TODO check workers metadata and modify if needed
|
#TODO check workers metadata and modify if needed
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -411,7 +417,6 @@ class decort_k8s(DecortController):
|
|||||||
),
|
),
|
||||||
description=dict(
|
description=dict(
|
||||||
type='str',
|
type='str',
|
||||||
default='Created by decort ansible module',
|
|
||||||
),
|
),
|
||||||
with_lb=dict(
|
with_lb=dict(
|
||||||
type='bool',
|
type='bool',
|
||||||
@@ -471,25 +476,25 @@ class decort_k8s(DecortController):
|
|||||||
|
|
||||||
self.is_k8s_stopped_or_will_be_stopped = (
|
self.is_k8s_stopped_or_will_be_stopped = (
|
||||||
(
|
(
|
||||||
self.k8s_info['techStatus'] == 'STOPPED'
|
self.k8s_info['tech_status'] == 'STOPPED'
|
||||||
and (
|
and (
|
||||||
self.aparams['state'] is None
|
self.aparams['state'] is None
|
||||||
or self.aparams['state'] in ('present', 'stopped')
|
or self.aparams['state'] in ('present', 'stopped')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
self.k8s_info['techStatus'] != 'STOPPED'
|
self.k8s_info['tech_status'] != 'STOPPED'
|
||||||
and self.aparams['state'] == 'stopped'
|
and self.aparams['state'] == 'stopped'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
aparam_sysctl = self.aparams['lb_sysctl']
|
aparam_sysctl = self.aparams['lb_sysctl']
|
||||||
if aparam_sysctl is not None:
|
if aparam_sysctl is not None:
|
||||||
_, lb_info = self._lb_get_by_id(lb_id=self.k8s_info['lbId'])
|
lb_model = self._lb_get_by_id(lb_id=self.k8s_info['lb_id'])
|
||||||
sysctl_with_str_values = {
|
sysctl_with_str_values = {
|
||||||
k: str(v) for k, v in aparam_sysctl.items()
|
k: str(v) for k, v in aparam_sysctl.items()
|
||||||
}
|
}
|
||||||
if sysctl_with_str_values != lb_info['sysctlParams']:
|
if sysctl_with_str_values != lb_model.sysctl_params:
|
||||||
self.message(
|
self.message(
|
||||||
'Check for parameter "lb_sysctl" failed: '
|
'Check for parameter "lb_sysctl" failed: '
|
||||||
'cannot change lb_sysctl for an existing cluster '
|
'cannot change lb_sysctl for an existing cluster '
|
||||||
@@ -501,7 +506,7 @@ class decort_k8s(DecortController):
|
|||||||
check_errors = True
|
check_errors = True
|
||||||
if (
|
if (
|
||||||
self.aparams['zone_id'] is not None
|
self.aparams['zone_id'] is not None
|
||||||
and self.aparams['zone_id'] != self.k8s_info['zoneId']
|
and self.aparams['zone_id'] != self.k8s_info['zone_id']
|
||||||
and not self.is_k8s_stopped_or_will_be_stopped
|
and not self.is_k8s_stopped_or_will_be_stopped
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
@@ -513,13 +518,11 @@ class decort_k8s(DecortController):
|
|||||||
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
||||||
if aparam_storage_policy_id is not None:
|
if aparam_storage_policy_id is not None:
|
||||||
computes_ids = []
|
computes_ids = []
|
||||||
for master_node in self.k8s_info['k8sGroups']['masters'][
|
for master_node in self.k8s_info['node_groups']['master']['vms']:
|
||||||
'detailedInfo'
|
|
||||||
]:
|
|
||||||
computes_ids.append(master_node['id'])
|
computes_ids.append(master_node['id'])
|
||||||
for wg in self.k8s_info['k8sGroups']['workers']:
|
for wg in self.k8s_info['node_groups']['worker']:
|
||||||
workers_ids = [
|
workers_ids = [
|
||||||
worker['id'] for worker in wg['detailedInfo']
|
worker['id'] for worker in wg['vms']
|
||||||
]
|
]
|
||||||
computes_ids.extend(workers_ids)
|
computes_ids.extend(workers_ids)
|
||||||
for compute_id in computes_ids:
|
for compute_id in computes_ids:
|
||||||
@@ -587,7 +590,7 @@ class decort_k8s(DecortController):
|
|||||||
)
|
)
|
||||||
elif (
|
elif (
|
||||||
aparam_storage_policy_id
|
aparam_storage_policy_id
|
||||||
not in self.rg_info['storage_policy_ids']
|
not in self.rg_info.storage_policy_ids
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
@@ -628,7 +631,9 @@ class decort_k8s(DecortController):
|
|||||||
if amodule.params['state'] in (
|
if amodule.params['state'] in (
|
||||||
'disabled', 'enabled', 'present', 'started', 'stopped'
|
'disabled', 'enabled', 'present', 'started', 'stopped'
|
||||||
):
|
):
|
||||||
self.k8s_restore(self.k8s_id)
|
self.sdk_checkmode(self.api.ca.k8s.restore)(
|
||||||
|
k8s_id=self.k8s_id,
|
||||||
|
)
|
||||||
self.action(disared_state=amodule.params['state'],
|
self.action(disared_state=amodule.params['state'],
|
||||||
preupdate=True)
|
preupdate=True)
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
|
|||||||
@@ -18,11 +18,9 @@ class decort_lb(DecortController):
|
|||||||
arg_amodule = self.amodule
|
arg_amodule = self.amodule
|
||||||
|
|
||||||
self.lb_id = 0
|
self.lb_id = 0
|
||||||
self.lb_facts = None
|
|
||||||
self.vins_id = 0
|
self.vins_id = 0
|
||||||
self.vins_facts = None
|
|
||||||
self.rg_id = 0
|
self.rg_id = 0
|
||||||
self.rg_facts = None
|
self.rg_model = None
|
||||||
self.default_server_check = "enabled"
|
self.default_server_check = "enabled"
|
||||||
self.default_alg = "roundrobin"
|
self.default_alg = "roundrobin"
|
||||||
self.default_settings = {
|
self.default_settings = {
|
||||||
@@ -38,22 +36,34 @@ class decort_lb(DecortController):
|
|||||||
self.is_lb_stopped_or_will_be_stopped: None | bool = None
|
self.is_lb_stopped_or_will_be_stopped: None | bool = None
|
||||||
|
|
||||||
if arg_amodule.params['lb_id']:
|
if arg_amodule.params['lb_id']:
|
||||||
self.lb_id, self.lb_facts = self.lb_find(arg_amodule.params['lb_id'])
|
_, self._lb_info = self.lb_find(lb_id=arg_amodule.params['lb_id'])
|
||||||
if not self.lb_id:
|
if self._lb_info is None:
|
||||||
self.result['failed'] = True
|
if arg_amodule.params['state'] == 'absent':
|
||||||
self.result['msg'] = "Specified LB ID {} not found."\
|
self.nop()
|
||||||
.format(arg_amodule.params['lb_id'])
|
self.exit()
|
||||||
self.amodule.fail_json(**self.result)
|
else:
|
||||||
self.rg_id = self.lb_facts['rgId']
|
self.message(
|
||||||
self.vins_id = self.lb_facts['vinsId']
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='lb',
|
||||||
|
id=arg_amodule.params['lb_id'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
self.lb_id = self._lb_info.id
|
||||||
|
self.rg_id = self._lb_info.rg_id
|
||||||
|
self.vins_id = self._lb_info.vins_id
|
||||||
|
|
||||||
elif arg_amodule.params['rg_id']:
|
elif arg_amodule.params['rg_id']:
|
||||||
self.rg_id, self.rg_facts = self.rg_find(0,arg_amodule.params['rg_id'], arg_rg_name="")
|
self.rg_id, self.rg_model = self.rg_find(
|
||||||
if not self.rg_id:
|
0,
|
||||||
|
arg_amodule.params['rg_id'],
|
||||||
|
arg_rg_name="",
|
||||||
|
)
|
||||||
|
if not self.rg_id or not self.rg_model:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['msg'] = "Specified RG ID {} not found.".format(arg_amodule.params['rg_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_model.account_id
|
||||||
|
|
||||||
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']:
|
||||||
@@ -67,24 +77,22 @@ class decort_lb(DecortController):
|
|||||||
self.result['msg'] = ("Current user does not have access to the requested account "
|
self.result['msg'] = ("Current user does not have access to the requested account "
|
||||||
"or non-existent account specified.")
|
"or non-existent account specified.")
|
||||||
self.amodule.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
self.rg_id, self.rg_facts = self.rg_find(self.acc_id,0, arg_rg_name=arg_amodule.params['rg_name'])
|
self.rg_id, self.rg_model = self.rg_find(
|
||||||
|
self.acc_id,
|
||||||
|
0,
|
||||||
|
arg_rg_name=arg_amodule.params['rg_name'],
|
||||||
|
)
|
||||||
|
|
||||||
if arg_amodule.params['vins_id']:
|
if arg_amodule.params['vins_id']:
|
||||||
self.vins_id, self.vins_facts = self.vins_find(
|
self.vins_id = self._vins_get_by_id(
|
||||||
vins_id=arg_amodule.params['vins_id']
|
vins_id=arg_amodule.params['vins_id']
|
||||||
)
|
).id
|
||||||
if not self.vins_id:
|
|
||||||
self.result['failed'] = True
|
|
||||||
self.result['msg'] = (
|
|
||||||
f'Specified ViNS ID {arg_amodule.params["vins_id"]}'
|
|
||||||
f' not found'
|
|
||||||
)
|
|
||||||
self.amodule.fail_json(**self.result)
|
|
||||||
elif arg_amodule.params['vins_name']:
|
elif arg_amodule.params['vins_name']:
|
||||||
self.vins_id, self.vins_facts = self.vins_find(
|
self.vins_id, _ = self.vins_find(
|
||||||
vins_id=arg_amodule.params['vins_id'],
|
vins_id=arg_amodule.params['vins_id'],
|
||||||
vins_name=arg_amodule.params['vins_name'],
|
vins_name=arg_amodule.params['vins_name'],
|
||||||
rg_id=self.rg_id)
|
rg_id=self.rg_id,
|
||||||
|
)
|
||||||
if not self.vins_id:
|
if not self.vins_id:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['msg'] = (
|
self.result['msg'] = (
|
||||||
@@ -94,10 +102,17 @@ class decort_lb(DecortController):
|
|||||||
self.amodule.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
|
|
||||||
if self.rg_id and arg_amodule.params['lb_name']:
|
if self.rg_id and arg_amodule.params['lb_name']:
|
||||||
self.lb_id, self.lb_facts = self.lb_find(0,arg_amodule.params['lb_name'],self.rg_id)
|
self.lb_id, self._lb_info = self.lb_find(
|
||||||
|
0,
|
||||||
|
arg_amodule.params['lb_name'],
|
||||||
|
self.rg_id,
|
||||||
|
)
|
||||||
|
|
||||||
if self.lb_id and self.lb_facts['status'] != 'DESTROYED':
|
if (
|
||||||
self.acc_id = self.lb_facts['accountId']
|
self._lb_info
|
||||||
|
and self._lb_info.status != sdk_types.LBStatus.DESTROYED
|
||||||
|
):
|
||||||
|
self.acc_id = self._lb_info.account_id
|
||||||
self.check_amodule_args_for_change()
|
self.check_amodule_args_for_change()
|
||||||
else:
|
else:
|
||||||
self.check_amodule_args_for_create()
|
self.check_amodule_args_for_create()
|
||||||
@@ -115,54 +130,73 @@ class decort_lb(DecortController):
|
|||||||
zone_id=self.aparams['zone_id'],
|
zone_id=self.aparams['zone_id'],
|
||||||
start=start_after_create,
|
start=start_after_create,
|
||||||
)
|
)
|
||||||
if self.lb_id and (self.amodule.params['backends'] or
|
if (
|
||||||
self.amodule.params['frontends']):
|
self.lb_id and (
|
||||||
self.lb_id, self.lb_facts = self.lb_find(0,self.amodule.params['lb_name'],self.rg_id)
|
self.amodule.params['backends']
|
||||||
|
or self.amodule.params['frontends']
|
||||||
|
)
|
||||||
|
):
|
||||||
|
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||||
self.lb_update(
|
self.lb_update(
|
||||||
lb_facts=self.lb_facts,
|
lb_model=self.lb_info,
|
||||||
aparam_backends=self.amodule.params['backends'],
|
aparam_backends=self.amodule.params['backends'],
|
||||||
aparam_frontends=self.amodule.params['frontends'],
|
aparam_frontends=self.amodule.params['frontends'],
|
||||||
aparam_servers=self.amodule.params['servers'],
|
aparam_servers=self.amodule.params['servers'],
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
def action(self,d_state='',restore=False):
|
def action(
|
||||||
if restore == True:
|
self,
|
||||||
self.lb_restore(lb_id=self.lb_id)
|
d_state='',
|
||||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
restore=False,
|
||||||
self.lb_state(self.lb_facts, 'enabled')
|
):
|
||||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
if restore:
|
||||||
|
self.sdk_checkmode(self.api.ca.lb.restore)(
|
||||||
|
lb_id=self.lb_id,
|
||||||
|
)
|
||||||
|
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||||
|
self.lb_state(self.lb_info, 'enabled')
|
||||||
|
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||||
|
|
||||||
self.lb_update(
|
self.lb_update(
|
||||||
lb_facts=self.lb_facts,
|
lb_model=self.lb_info,
|
||||||
aparam_backends=self.amodule.params['backends'],
|
aparam_backends=self.amodule.params['backends'],
|
||||||
aparam_frontends=self.amodule.params['frontends'],
|
aparam_frontends=self.amodule.params['frontends'],
|
||||||
aparam_servers=self.amodule.params['servers'],
|
aparam_servers=self.amodule.params['servers'],
|
||||||
aparam_sysctl=self.aparams['sysctl'],
|
aparam_sysctl=self.aparams['sysctl'],
|
||||||
)
|
)
|
||||||
|
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||||
|
|
||||||
if d_state != '':
|
if d_state != '':
|
||||||
self.lb_state(self.lb_facts, d_state)
|
self.lb_state(self.lb_info, d_state)
|
||||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||||
|
|
||||||
if (d_state == 'enabled' and
|
if (
|
||||||
self.lb_facts.get('status') == 'ENABLED' and
|
d_state == 'enabled'
|
||||||
self.lb_facts.get('techStatus') == 'STOPPED'):
|
and self.lb_info.status == sdk_types.LBStatus.ENABLED
|
||||||
self.lb_state(self.lb_facts, 'started')
|
and self.lb_info.tech_status == sdk_types.LBTechStatus.STOPPED
|
||||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
):
|
||||||
|
self.lb_state(self.lb_info, 'started')
|
||||||
|
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||||
|
|
||||||
aparam_zone_id = self.aparams['zone_id']
|
aparam_zone_id = self.aparams['zone_id']
|
||||||
if aparam_zone_id is not None and aparam_zone_id != self.lb_facts['zoneId']:
|
if (
|
||||||
self.lb_migrate_to_zone(
|
aparam_zone_id is not None
|
||||||
lb_id=self.lb_id,
|
and aparam_zone_id != self.lb_info.zone_id
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.lb.migrate_to_zone)(
|
||||||
|
lb_id=self.lb_info.id,
|
||||||
zone_id=aparam_zone_id,
|
zone_id=aparam_zone_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
self.lb_delete(self.lb_id, self.amodule.params['permanently'])
|
self.sdk_checkmode(self.api.ca.lb.delete)(
|
||||||
self.lb_id, self.lb_facts = self._lb_get_by_id(self.lb_id)
|
lb_id=self.lb_id,
|
||||||
|
permanently=self.amodule.params['permanently'],
|
||||||
|
)
|
||||||
|
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
def nop(self):
|
def nop(self):
|
||||||
@@ -173,30 +207,39 @@ class decort_lb(DecortController):
|
|||||||
"""
|
"""
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
if self.lb_id:
|
if self._lb_info:
|
||||||
self.result['msg'] = ("No state change required for LB ID {} because of its "
|
self.result['msg'] = (
|
||||||
"current status '{}'.").format(self.lb_id, self.lb_facts['status'])
|
f'No state change required for LB ID {self._lb_info.id} '
|
||||||
|
f'because of its current status "{self._lb_info.status}".'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
self.result['msg'] = (
|
||||||
"non-existent LB instance.").format(self.amodule.params['state'])
|
f'No state change to "{self.amodule.params['state']}" '
|
||||||
|
f'can be done for non-existent LB instance.'
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
def error(self):
|
def error(self):
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
if self.vins_id:
|
if self._lb_info:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = ("Invalid target state '{}' requested for LB ID {} in the "
|
self.result['msg'] = (
|
||||||
"current status '{}'").format(self.lb_id,
|
f'Invalid target state "{self.amodule.params['state']}" '
|
||||||
self.amodule.params['state'],
|
f'requested for LB ID {self._lb_info.id} in the current status '
|
||||||
self.lb_facts['status'])
|
f'"{self._lb_info.status}".'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = ("Invalid target state '{}' requested for non-existent "
|
self.result['msg'] = (
|
||||||
"LB name '{}'").format(self.amodule.params['state'],
|
f'Invalid target state "{self.amodule.params['state']}" '
|
||||||
self.amodule.params['lb_name'])
|
f'requested for non-existent LB name '
|
||||||
|
f'"{self.amodule.params['lb_name']}".'
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
def package_facts(self, arg_check_mode=False):
|
def package_facts(self, arg_check_mode=False):
|
||||||
"""Package a dictionary of LB facts according to the decort_lb module specification.
|
"""Package a dictionary of LB facts according to the decort_lb module specification.
|
||||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||||
@@ -205,34 +248,16 @@ class decort_lb(DecortController):
|
|||||||
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode
|
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ret_dict = dict(id=0,
|
ret_dict = {}
|
||||||
name="none",
|
|
||||||
state="CHECK_MODE",
|
|
||||||
sysctl={},
|
|
||||||
)
|
|
||||||
|
|
||||||
if arg_check_mode:
|
if arg_check_mode:
|
||||||
# in check mode return immediately with the default values
|
# in check mode return immediately with the default values
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
if self.lb_facts is None:
|
if self._lb_info is None:
|
||||||
# if void facts provided - change state value to ABSENT and return
|
|
||||||
ret_dict['state'] = "ABSENT"
|
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
ret_dict['id'] = self.lb_facts['id']
|
return self._lb_info.model_dump()
|
||||||
ret_dict['name'] = self.lb_facts['name']
|
|
||||||
ret_dict['state'] = self.lb_facts['status']
|
|
||||||
ret_dict['account_id'] = self.lb_facts['accountId']
|
|
||||||
ret_dict['rg_id'] = self.lb_facts['rgId']
|
|
||||||
ret_dict['gid'] = self.lb_facts['gid']
|
|
||||||
if self.amodule.params['state']!="absent":
|
|
||||||
ret_dict['backends'] = self.lb_facts['backends']
|
|
||||||
ret_dict['frontends'] = self.lb_facts['frontends']
|
|
||||||
ret_dict['sysctl'] = self.lb_facts['sysctlParams']
|
|
||||||
ret_dict['zone_id'] = self.lb_facts['zoneId']
|
|
||||||
ret_dict['tech_status'] = self.lb_facts['techStatus']
|
|
||||||
return ret_dict
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amodule_init_args(self) -> dict:
|
def amodule_init_args(self) -> dict:
|
||||||
@@ -321,18 +346,16 @@ class decort_lb(DecortController):
|
|||||||
|
|
||||||
def check_amodule_args_for_change(self):
|
def check_amodule_args_for_change(self):
|
||||||
check_errors = False
|
check_errors = False
|
||||||
|
self.is_lb_stopped_or_will_be_stopped = (
|
||||||
lb_info: dict = self.lb_facts
|
|
||||||
self.is_lb_stopped_or_will_be_stopped = (
|
|
||||||
(
|
(
|
||||||
lb_info['techStatus'] == 'STOPPED'
|
self.lb_info.tech_status == sdk_types.LBTechStatus.STOPPED
|
||||||
and (
|
and (
|
||||||
self.aparams['state'] is None
|
self.aparams['state'] is None
|
||||||
or self.aparams['state'] in ('present', 'stopped')
|
or self.aparams['state'] in ('present', 'stopped')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
lb_info['techStatus'] != 'STOPPED'
|
self.lb_info.tech_status != sdk_types.LBTechStatus.STOPPED
|
||||||
and self.aparams['state'] == 'stopped'
|
and self.aparams['state'] == 'stopped'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -341,7 +364,7 @@ class decort_lb(DecortController):
|
|||||||
check_errors = True
|
check_errors = True
|
||||||
if (
|
if (
|
||||||
self.aparams['zone_id'] is not None
|
self.aparams['zone_id'] is not None
|
||||||
and self.aparams['zone_id'] != lb_info['zoneId']
|
and self.aparams['zone_id'] != self.lb_info.zone_id
|
||||||
and not self.is_lb_stopped_or_will_be_stopped
|
and not self.is_lb_stopped_or_will_be_stopped
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
@@ -364,18 +387,32 @@ class decort_lb(DecortController):
|
|||||||
@DecortController.handle_sdk_exceptions
|
@DecortController.handle_sdk_exceptions
|
||||||
def run(self):
|
def run(self):
|
||||||
amodule = self.amodule
|
amodule = self.amodule
|
||||||
if self.lb_id:
|
if self._lb_info:
|
||||||
if self.lb_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING","DESTROYING","RESTORING"]:
|
if self._lb_info.status in [
|
||||||
|
sdk_types.LBStatus.MODELED,
|
||||||
|
sdk_types.LBStatus.DISABLING,
|
||||||
|
sdk_types.LBStatus.ENABLING,
|
||||||
|
sdk_types.LBStatus.DELETING,
|
||||||
|
sdk_types.LBStatus.DESTROYING,
|
||||||
|
sdk_types.LBStatus.RESTORING,
|
||||||
|
]:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = ("No change can be done for existing LB ID {} because of its current "
|
self.result['msg'] = (
|
||||||
"status '{}'").format(self.lb_id, self.lb_facts['status'])
|
f'No change can be done for existing LB ID '
|
||||||
elif self.lb_facts['status'] in ('DISABLED', 'ENABLED', 'CREATED'):
|
f'{self._lb_info.id} because of its current status '
|
||||||
|
f'"{self._lb_info.status.name}".'
|
||||||
|
)
|
||||||
|
elif self._lb_info.status in [
|
||||||
|
sdk_types.LBStatus.DISABLED,
|
||||||
|
sdk_types.LBStatus.ENABLED,
|
||||||
|
sdk_types.LBStatus.CREATED,
|
||||||
|
]:
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
self.delete()
|
self.delete()
|
||||||
else:
|
else:
|
||||||
self.action(d_state=amodule.params['state'])
|
self.action(d_state=amodule.params['state'])
|
||||||
elif self.lb_facts['status'] == "DELETED":
|
elif self._lb_info.status == sdk_types.LBStatus.DELETED:
|
||||||
if amodule.params['state'] == 'present':
|
if amodule.params['state'] == 'present':
|
||||||
self.action(restore=True)
|
self.action(restore=True)
|
||||||
elif amodule.params['state'] == 'enabled':
|
elif amodule.params['state'] == 'enabled':
|
||||||
@@ -385,7 +422,7 @@ class decort_lb(DecortController):
|
|||||||
self.delete()
|
self.delete()
|
||||||
elif amodule.params['state'] == 'disabled':
|
elif amodule.params['state'] == 'disabled':
|
||||||
self.error()
|
self.error()
|
||||||
elif self.lb_facts['status'] == "DESTROYED":
|
elif self._lb_info.status == sdk_types.LBStatus.DESTROYED:
|
||||||
if amodule.params['state'] in ('present', 'enabled'):
|
if amodule.params['state'] in ('present', 'enabled'):
|
||||||
self.create()
|
self.create()
|
||||||
elif amodule.params['state'] == 'absent':
|
elif amodule.params['state'] == 'absent':
|
||||||
@@ -407,7 +444,7 @@ class decort_lb(DecortController):
|
|||||||
amodule.fail_json(**self.result)
|
amodule.fail_json(**self.result)
|
||||||
else:
|
else:
|
||||||
if self.result['changed']:
|
if self.result['changed']:
|
||||||
_, self.lb_facts = self.lb_find(lb_id=self.lb_id)
|
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||||
self.result['facts'] = self.package_facts(amodule.check_mode)
|
self.result['facts'] = self.package_facts(amodule.check_mode)
|
||||||
amodule.exit_json(**self.result)
|
amodule.exit_json(**self.result)
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,13 @@ class decort_pfw(DecortController):
|
|||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def decort_pfw_package_facts(self, comp_facts, vins_facts, pfw_facts, check_mode=False):
|
def decort_pfw_package_facts(
|
||||||
|
self,
|
||||||
|
comp_facts,
|
||||||
|
vins_model: sdk_types.CloudapiVinsGetResultModel,
|
||||||
|
pfw_facts,
|
||||||
|
check_mode=False,
|
||||||
|
):
|
||||||
"""Package a dictionary of PFW rules facts according to the decort_pfw module specification.
|
"""Package a dictionary of PFW rules facts according to the decort_pfw module specification.
|
||||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||||
the module run.
|
the module run.
|
||||||
@@ -68,9 +74,13 @@ class decort_pfw(DecortController):
|
|||||||
ret_dict['state'] = "ABSENT"
|
ret_dict['state'] = "ABSENT"
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
|
gw_vnf = vins_model.vnfs.gw
|
||||||
ret_dict['compute_id'] = comp_facts['id']
|
ret_dict['compute_id'] = comp_facts['id']
|
||||||
ret_dict['vins_id'] = vins_facts['id']
|
ret_dict['vins_id'] = vins_model.id
|
||||||
ret_dict['public_ip'] = vins_facts['vnfs']['GW']['config']['ext_net_ip']
|
if gw_vnf:
|
||||||
|
ret_dict['public_ip'] = gw_vnf.config.ext_net_ip
|
||||||
|
else:
|
||||||
|
raise RuntimeError('VINS GW VNF must exist.')
|
||||||
|
|
||||||
if len(pfw_facts) != 0:
|
if len(pfw_facts) != 0:
|
||||||
ret_dict['state'] = 'PRESENT'
|
ret_dict['state'] = 'PRESENT'
|
||||||
@@ -106,16 +116,15 @@ class decort_pfw(DecortController):
|
|||||||
self.result['msg'] = "Cannot find specified Compute ID {}.".format(amodule.params['compute_id'])
|
self.result['msg'] = "Cannot find specified Compute ID {}.".format(amodule.params['compute_id'])
|
||||||
amodule.fail_json(**self.result)
|
amodule.fail_json(**self.result)
|
||||||
|
|
||||||
validated_vins_id, vins_facts = self.vins_find(amodule.params['vins_id'])
|
vins_model = self._vins_get_by_id(vins_id=amodule.params['vins_id'])
|
||||||
if not validated_vins_id:
|
|
||||||
self.result['failed'] = True
|
|
||||||
self.result['msg'] = "Cannot find specified ViNS ID {}.".format(amodule.params['vins_id'])
|
|
||||||
amodule.fail_json(**self.result)
|
|
||||||
|
|
||||||
gw_vnf_facts = vins_facts['vnfs'].get('GW')
|
gw_vnf = vins_model.vnfs.gw
|
||||||
if not gw_vnf_facts or gw_vnf_facts['status'] == "DESTROYED":
|
if not gw_vnf or gw_vnf.status == sdk_types.VNFStatus.DESTROYED:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['msg'] = "ViNS ID {} does not have a configured external connection.".format(validated_vins_id)
|
self.result['msg'] = (
|
||||||
|
f'ViNS ID {vins_model.id} does not '
|
||||||
|
f'have a configured external connection.'
|
||||||
|
)
|
||||||
amodule.fail_json(**self.result)
|
amodule.fail_json(**self.result)
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -124,12 +133,20 @@ class decort_pfw(DecortController):
|
|||||||
|
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
# ignore amodule.params['rules'] and remove all rules associated with this Compute
|
# ignore amodule.params['rules'] and remove all rules associated with this Compute
|
||||||
pfw_facts = self.pfw_configure(comp_facts, vins_facts, None)
|
pfw_facts = self.pfw_configure(
|
||||||
|
comp_facts,
|
||||||
|
vins_model,
|
||||||
|
None,
|
||||||
|
)
|
||||||
elif amodule.params['rules'] is not None:
|
elif amodule.params['rules'] is not None:
|
||||||
# manage PFW rules accodring to the module arguments
|
# manage PFW rules accodring to the module arguments
|
||||||
pfw_facts = self.pfw_configure(comp_facts, vins_facts, amodule.params['rules'])
|
pfw_facts = self.pfw_configure(
|
||||||
|
comp_facts,
|
||||||
|
vins_model,
|
||||||
|
amodule.params['rules'],
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
pfw_facts = self._pfw_get(comp_facts['id'], vins_facts['id'])
|
pfw_facts = self._pfw_get(comp_facts['id'], vins_model.id)
|
||||||
|
|
||||||
#
|
#
|
||||||
# complete module run
|
# complete module run
|
||||||
@@ -138,7 +155,12 @@ class decort_pfw(DecortController):
|
|||||||
amodule.fail_json(**self.result)
|
amodule.fail_json(**self.result)
|
||||||
else:
|
else:
|
||||||
# prepare PFW facts to be returned as part of self.result and then call exit_json(...)
|
# prepare PFW facts to be returned as part of self.result and then call exit_json(...)
|
||||||
self.result['facts'] = self.decort_pfw_package_facts(comp_facts, vins_facts, pfw_facts, amodule.check_mode)
|
self.result['facts'] = self.decort_pfw_package_facts(
|
||||||
|
comp_facts,
|
||||||
|
vins_model,
|
||||||
|
pfw_facts,
|
||||||
|
amodule.check_mode,
|
||||||
|
)
|
||||||
amodule.exit_json(**self.result)
|
amodule.exit_json(**self.result)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ class decort_rg(DecortController):
|
|||||||
amodule = self.amodule
|
amodule = self.amodule
|
||||||
|
|
||||||
self.validated_acc_id = 0
|
self.validated_acc_id = 0
|
||||||
self.validated_rg_id = 0
|
self.rg_id: int = 0
|
||||||
self.validated_rg_facts = None
|
|
||||||
|
|
||||||
if amodule.params['rg_id'] is None:
|
if amodule.params['rg_id'] is None:
|
||||||
if self.amodule.params['account_id']:
|
if self.amodule.params['account_id']:
|
||||||
@@ -42,13 +41,15 @@ class decort_rg(DecortController):
|
|||||||
else:
|
else:
|
||||||
self.rg_should_exist = False
|
self.rg_should_exist = False
|
||||||
|
|
||||||
if self.validated_rg_id and self.rg_facts['status'] != 'DESTROYED':
|
if self.rg_id and (
|
||||||
|
self.rg_info.status != sdk_types.ResourceGroupStatus.DESTROYED
|
||||||
|
):
|
||||||
self.check_amodule_args_for_change()
|
self.check_amodule_args_for_change()
|
||||||
|
|
||||||
def get_info(self):
|
def get_info(self):
|
||||||
# If this is the first getting info
|
# If this is the first getting info
|
||||||
if not self.validated_rg_id:
|
if self._rg_info is None:
|
||||||
self.validated_rg_id, self.rg_facts = self.rg_find(
|
self.rg_id, self._rg_info = self.rg_find(
|
||||||
arg_account_id=self.validated_acc_id,
|
arg_account_id=self.validated_acc_id,
|
||||||
arg_rg_id=self.aparams['rg_id'],
|
arg_rg_id=self.aparams['rg_id'],
|
||||||
arg_rg_name=self.aparams['rg_name'],
|
arg_rg_name=self.aparams['rg_name'],
|
||||||
@@ -61,16 +62,16 @@ class decort_rg(DecortController):
|
|||||||
if self.amodule.check_mode:
|
if self.amodule.check_mode:
|
||||||
return
|
return
|
||||||
|
|
||||||
_, self.rg_facts = self.rg_find(arg_rg_id=self.validated_rg_id)
|
_, self._rg_info = self.rg_find(arg_rg_id=self.rg_id)
|
||||||
|
|
||||||
def access(self):
|
def access(self):
|
||||||
should_change_access = False
|
should_change_access = False
|
||||||
acc_granted = False
|
acc_granted = False
|
||||||
for rg_item in self.rg_facts['acl']:
|
for rg_item in self.rg_info.acl:
|
||||||
if rg_item['userGroupId'] == self.amodule.params['access']['user']:
|
if rg_item.user_name == self.amodule.params['access']['user']:
|
||||||
acc_granted = True
|
acc_granted = True
|
||||||
if self.amodule.params['access']['action'] == 'grant':
|
if self.amodule.params['access']['action'] == 'grant':
|
||||||
if rg_item['right'] != self.amodule.params['access']['right']:
|
if rg_item.access_type.value != self.amodule.params['access']['right']:
|
||||||
should_change_access = True
|
should_change_access = True
|
||||||
if self.amodule.params['access']['action'] == 'revoke':
|
if self.amodule.params['access']['action'] == 'revoke':
|
||||||
should_change_access = True
|
should_change_access = True
|
||||||
@@ -78,19 +79,30 @@ class decort_rg(DecortController):
|
|||||||
should_change_access = True
|
should_change_access = True
|
||||||
|
|
||||||
if should_change_access == True:
|
if should_change_access == True:
|
||||||
self.rg_access(self.validated_rg_id, self.amodule.params['access'])
|
if self.amodule.params['access']['action'] == "grant":
|
||||||
self.rg_facts['access'] = self.amodule.params['access']
|
self.sdk_checkmode(self.api.ca.rg.access_grant)(
|
||||||
|
access_type=sdk_types.AccessTypeForSet(
|
||||||
|
self.amodule.params['access']['right']
|
||||||
|
),
|
||||||
|
rg_id=self.rg_id,
|
||||||
|
user_name=self.amodule.params['access']['user'],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.sdk_checkmode(self.api.ca.rg.access_revoke)(
|
||||||
|
rg_id=self.rg_id,
|
||||||
|
user_name=self.amodule.params['access']['user'],
|
||||||
|
)
|
||||||
self.rg_should_exist = True
|
self.rg_should_exist = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def error(self):
|
def error(self):
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
if self.validated_rg_id > 0:
|
if self.rg_id:
|
||||||
self.result['msg'] = ("Invalid target state '{}' requested for rg ID {} in the "
|
self.result['msg'] = ("Invalid target state '{}' requested for rg ID {} in the "
|
||||||
"current status '{}'.").format(self.validated_rg_id,
|
"current status '{}'.").format(self.rg_id,
|
||||||
self.amodule.params['state'],
|
self.amodule.params['state'],
|
||||||
self.rg_facts['status'])
|
self.rg_info.status.value)
|
||||||
else:
|
else:
|
||||||
self.result['msg'] = ("Invalid target state '{}' requested for non-existent rg name '{}' "
|
self.result['msg'] = ("Invalid target state '{}' requested for non-existent rg name '{}' "
|
||||||
"in account ID {} ").format(self.amodule.params['state'],
|
"in account ID {} ").format(self.amodule.params['state'],
|
||||||
@@ -99,20 +111,31 @@ class decort_rg(DecortController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
resources = self.rg_facts['Resources']['Reserved']
|
try:
|
||||||
|
rg_res_model = (
|
||||||
|
self.api.cloudapi.rg.get_resource_consumption(rg_id=self.rg_id)
|
||||||
|
)
|
||||||
|
except sdk_exceptions.RequestException as e:
|
||||||
|
self.message(
|
||||||
|
msg=(
|
||||||
|
f'Failed to get RG Resources by ID {self.rg_id}: {e}'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
reserved_resources = rg_res_model.reserved
|
||||||
incorrect_quota = dict(Requested=dict(),
|
incorrect_quota = dict(Requested=dict(),
|
||||||
Reserved=dict(),)
|
Reserved=dict(),)
|
||||||
query_key_map = dict(
|
query_key_map = dict(
|
||||||
cpu='cpu',
|
cpu='cpu_count',
|
||||||
ram='ram',
|
ram='ram_size_mb',
|
||||||
disk='disksize',
|
disk='storage_size_gb_by_real_usage',
|
||||||
ext_ips='extips',
|
ext_ips='ext_ip_count',
|
||||||
storage_policies='policies',
|
storage_policies='storage_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 quota_item == 'storage_policies':
|
if quota_item == 'storage_policies':
|
||||||
rg_storage_policies = resources[query_key_map[quota_item]]
|
rg_storage_policies = reserved_resources.storage_policies
|
||||||
aparam_storage_policies = self.amodule.params['quotas'][
|
aparam_storage_policies = self.amodule.params['quotas'][
|
||||||
quota_item
|
quota_item
|
||||||
]
|
]
|
||||||
@@ -124,32 +147,35 @@ class decort_rg(DecortController):
|
|||||||
if (
|
if (
|
||||||
rg_storage_policy
|
rg_storage_policy
|
||||||
and aparam_storage_policy['storage_size_gb']
|
and aparam_storage_policy['storage_size_gb']
|
||||||
< rg_storage_policy['disksize']
|
< rg_storage_policy.storage_size_gb_by_real_usage
|
||||||
):
|
):
|
||||||
incorrect_quota['Requested'][quota_item] = (
|
incorrect_quota['Requested'][quota_item] = (
|
||||||
self.amodule.params['quotas'][quota_item]
|
self.amodule.params['quotas'][quota_item]
|
||||||
)
|
)
|
||||||
incorrect_quota['Reserved'][quota_item] = (
|
incorrect_quota['Reserved'][quota_item] = (
|
||||||
resources[query_key_map[quota_item]]
|
getattr(
|
||||||
|
reserved_resources,
|
||||||
|
query_key_map[quota_item]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
elif (
|
elif (
|
||||||
self.amodule.params['quotas'][quota_item]
|
self.amodule.params['quotas'][quota_item]
|
||||||
< resources[query_key_map[quota_item]]
|
< getattr(reserved_resources, query_key_map[quota_item])
|
||||||
):
|
):
|
||||||
incorrect_quota['Requested'][quota_item] = (
|
incorrect_quota['Requested'][quota_item] = (
|
||||||
self.amodule.params['quotas'][quota_item]
|
self.amodule.params['quotas'][quota_item]
|
||||||
)
|
)
|
||||||
incorrect_quota['Reserved'][quota_item] = (
|
incorrect_quota['Reserved'][quota_item] = (
|
||||||
resources[query_key_map[quota_item]]
|
getattr(reserved_resources, query_key_map[quota_item])
|
||||||
)
|
)
|
||||||
|
|
||||||
if incorrect_quota['Requested']:
|
if reserved_resources and incorrect_quota['Requested']:
|
||||||
self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota)
|
self.result['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,
|
rg_model=self.rg_info,
|
||||||
arg_quotas=self.amodule.params['quotas'],
|
arg_quotas=self.amodule.params['quotas'],
|
||||||
arg_res_types=self.amodule.params['resType'],
|
arg_res_types=self.amodule.params['resType'],
|
||||||
arg_newname=self.amodule.params['rename'],
|
arg_newname=self.amodule.params['rename'],
|
||||||
@@ -160,13 +186,15 @@ class decort_rg(DecortController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def setDefNet(self):
|
def setDefNet(self):
|
||||||
rg_def_net_type = self.rg_facts['def_net_type']
|
rg_def_net_type = self.rg_info.default_net_type.value
|
||||||
rg_def_net_id = self.rg_facts['def_net_id']
|
rg_def_net_id = self.rg_info.default_net_id
|
||||||
aparam_def_net_type = self.aparams['def_netType']
|
aparam_def_net_type = self.aparams['def_netType']
|
||||||
aparam_def_net_id = self.aparams['def_netId']
|
aparam_def_net_id = self.aparams['def_netId']
|
||||||
|
|
||||||
need_to_reset = (aparam_def_net_type == 'NONE'
|
need_to_reset = (
|
||||||
and rg_def_net_type != aparam_def_net_type)
|
aparam_def_net_type == sdk_types.RGDefaultNetType.NONE
|
||||||
|
and rg_def_net_type != aparam_def_net_type
|
||||||
|
)
|
||||||
|
|
||||||
need_to_change = False
|
need_to_change = False
|
||||||
if aparam_def_net_id is not None:
|
if aparam_def_net_id is not None:
|
||||||
@@ -174,16 +202,26 @@ class decort_rg(DecortController):
|
|||||||
or aparam_def_net_type != rg_def_net_type)
|
or aparam_def_net_type != rg_def_net_type)
|
||||||
|
|
||||||
if need_to_reset or need_to_change:
|
if need_to_reset or need_to_change:
|
||||||
self.rg_setDefNet(
|
if aparam_def_net_type == sdk_types.RGDefaultNetType.NONE:
|
||||||
arg_rg_id=self.validated_rg_id,
|
self.sdk_checkmode(self.api.ca.rg.remove_def_net)(
|
||||||
arg_net_type=aparam_def_net_type,
|
rg_id=self.rg_id,
|
||||||
arg_net_id=aparam_def_net_id,
|
)
|
||||||
)
|
else:
|
||||||
|
if aparam_def_net_type == sdk_types.RGDefaultNetType.PRIVATE:
|
||||||
|
net_type = sdk_types.RGDefaultNetTypeForSet.PRIVATE
|
||||||
|
elif aparam_def_net_type == sdk_types.RGDefaultNetType.PUBLIC:
|
||||||
|
net_type = sdk_types.RGDefaultNetTypeForSet.PUBLIC
|
||||||
|
|
||||||
|
self.sdk_checkmode(self.api.ca.rg.set_def_net)(
|
||||||
|
net_type=net_type,
|
||||||
|
rg_id=self.rg_id,
|
||||||
|
net_id=aparam_def_net_id,
|
||||||
|
)
|
||||||
self.rg_should_exist = True
|
self.rg_should_exist = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
self.validated_rg_id = self.rg_provision(
|
self.rg_id = self.rg_provision(
|
||||||
self.validated_acc_id,
|
self.validated_acc_id,
|
||||||
self.amodule.params['rg_name'],
|
self.amodule.params['rg_name'],
|
||||||
self.amodule.params['owner'],
|
self.amodule.params['owner'],
|
||||||
@@ -196,12 +234,13 @@ class decort_rg(DecortController):
|
|||||||
self.amodule.params['quotas'],
|
self.amodule.params['quotas'],
|
||||||
"", # this is location code. TODO: add module argument
|
"", # this is location code. TODO: add module argument
|
||||||
sdn_access_group_id=self.aparams['sdn_access_group_id'],
|
sdn_access_group_id=self.aparams['sdn_access_group_id'],
|
||||||
|
enable_default_gateway=self.aparams['enable_default_gateway'],
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.validated_rg_id:
|
if self.rg_id:
|
||||||
self.validated_rg_id, self.rg_facts = self.rg_find(
|
self.rg_id, self._rg_info = self.rg_find(
|
||||||
arg_account_id=self.validated_acc_id,
|
arg_account_id=self.validated_acc_id,
|
||||||
arg_rg_id=self.validated_rg_id,
|
arg_rg_id=self.rg_id,
|
||||||
arg_rg_name="",
|
arg_rg_name="",
|
||||||
arg_check_state=False
|
arg_check_state=False
|
||||||
)
|
)
|
||||||
@@ -209,31 +248,30 @@ class decort_rg(DecortController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
self.rg_enable(self.validated_rg_id,
|
|
||||||
self.amodule.params['state'])
|
|
||||||
if self.amodule.params['state'] == "enabled":
|
if self.amodule.params['state'] == "enabled":
|
||||||
self.rg_facts['status'] = 'CREATED'
|
self.sdk_checkmode(self.api.ca.rg.enable)(
|
||||||
else:
|
rg_id=self.rg_id,
|
||||||
self.rg_facts['status'] = 'DISABLED'
|
)
|
||||||
|
elif self.amodule.params['state'] == "disabled":
|
||||||
|
self.sdk_checkmode(self.api.ca.rg.disable)(
|
||||||
|
rg_id=self.rg_id,
|
||||||
|
)
|
||||||
self.rg_should_exist = True
|
self.rg_should_exist = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def restore(self):
|
def restore(self):
|
||||||
self.rg_restore(self.validated_rg_id)
|
self.sdk_checkmode(self.api.ca.rg.restore)(
|
||||||
self.rg_facts['status'] = 'DISABLED'
|
rg_id=self.rg_id,
|
||||||
|
)
|
||||||
self.rg_should_exist = True
|
self.rg_should_exist = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
self.rg_delete(
|
self.sdk_checkmode(self.api.ca.rg.delete)(
|
||||||
rg_id=self.validated_rg_id,
|
rg_id=self.rg_id,
|
||||||
permanently=self.amodule.params['permanently'],
|
permanently=self.amodule.params['permanently'],
|
||||||
recursively=self.aparams['recursive_deletion'],
|
recursively=self.aparams['recursive_deletion'],
|
||||||
)
|
)
|
||||||
if self.amodule.params['permanently'] == True:
|
|
||||||
self.rg_facts['status'] = 'DESTROYED'
|
|
||||||
else:
|
|
||||||
self.rg_facts['status'] = 'DELETED'
|
|
||||||
self.rg_should_exist = False
|
self.rg_should_exist = False
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -259,23 +297,7 @@ class decort_rg(DecortController):
|
|||||||
# ret_dict['state'] = "ABSENT"
|
# ret_dict['state'] = "ABSENT"
|
||||||
# return ret_dict
|
# return ret_dict
|
||||||
|
|
||||||
ret_dict['id'] = self.rg_facts['id']
|
return self.rg_info.model_dump()
|
||||||
ret_dict['name'] = self.rg_facts['name']
|
|
||||||
ret_dict['state'] = self.rg_facts['status']
|
|
||||||
ret_dict['account_id'] = self.rg_facts['accountId']
|
|
||||||
ret_dict['gid'] = self.rg_facts['gid']
|
|
||||||
ret_dict['quota'] = self.rg_facts['resourceLimits']
|
|
||||||
ret_dict['resTypes'] = self.rg_facts['resourceTypes']
|
|
||||||
ret_dict['defNetId'] = self.rg_facts['def_net_id']
|
|
||||||
ret_dict['defNetType'] = self.rg_facts['def_net_type']
|
|
||||||
ret_dict['ViNS'] = self.rg_facts['vins']
|
|
||||||
ret_dict['computes'] = self.rg_facts['vms']
|
|
||||||
ret_dict['uniqPools'] = self.rg_facts['uniqPools']
|
|
||||||
ret_dict['description'] = self.rg_facts['desc']
|
|
||||||
ret_dict['sdn_access_group_id'] = self.rg_facts['sdn_access_group_id']
|
|
||||||
ret_dict['storage_policy_ids'] = self.rg_facts['storage_policy_ids']
|
|
||||||
|
|
||||||
return ret_dict
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amodule_init_args(self) -> dict:
|
def amodule_init_args(self) -> dict:
|
||||||
@@ -290,6 +312,24 @@ class decort_rg(DecortController):
|
|||||||
),
|
),
|
||||||
access=dict(
|
access=dict(
|
||||||
type='dict',
|
type='dict',
|
||||||
|
options=dict(
|
||||||
|
action=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
choices=[
|
||||||
|
'grant',
|
||||||
|
'revoke',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
user=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
right=dict(
|
||||||
|
type='str',
|
||||||
|
choices=sdk_types.AccessTypeForSet._member_names_,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
description=dict(
|
description=dict(
|
||||||
type='str',
|
type='str',
|
||||||
@@ -297,12 +337,8 @@ class decort_rg(DecortController):
|
|||||||
),
|
),
|
||||||
def_netType=dict(
|
def_netType=dict(
|
||||||
type='str',
|
type='str',
|
||||||
choices=[
|
choices=sdk_types.RGDefaultNetType._member_names_,
|
||||||
'PRIVATE',
|
default=sdk_types.RGDefaultNetType.PRIVATE.name,
|
||||||
'PUBLIC',
|
|
||||||
'NONE',
|
|
||||||
],
|
|
||||||
default='PRIVATE',
|
|
||||||
),
|
),
|
||||||
def_netId=dict(
|
def_netId=dict(
|
||||||
type='int',
|
type='int',
|
||||||
@@ -375,6 +411,9 @@ class decort_rg(DecortController):
|
|||||||
sdn_access_group_id=dict(
|
sdn_access_group_id=dict(
|
||||||
type='str',
|
type='str',
|
||||||
),
|
),
|
||||||
|
enable_default_gateway=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
@@ -386,13 +425,73 @@ class decort_rg(DecortController):
|
|||||||
self.aparams['sdn_access_group_id'] is not None
|
self.aparams['sdn_access_group_id'] is not None
|
||||||
and (
|
and (
|
||||||
self.aparams['sdn_access_group_id']
|
self.aparams['sdn_access_group_id']
|
||||||
!= self.rg_facts['sdn_access_group_id']
|
!= self.rg_info.sdn_access_group_id
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
self.message(
|
self.message(
|
||||||
'Check for parameter "sdn_access_group_id" failed: '
|
'Check for parameter "sdn_access_group_id" failed: '
|
||||||
'cannot change sdn_access_group_id for an existing resource '
|
'cannot change sdn_access_group_id for an existing resource '
|
||||||
f'group ID {self.validated_rg_id}.'
|
f'group ID {self.rg_id}.'
|
||||||
|
)
|
||||||
|
check_errors = True
|
||||||
|
|
||||||
|
if self.aparams['enable_default_gateway'] is not None:
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
self.aparams['def_netType'] is None
|
||||||
|
and self.rg_info.default_net_type != sdk_types.RGDefaultNetType.PRIVATE # noqa: E501
|
||||||
|
) or (
|
||||||
|
self.aparams['def_netType'] is not None
|
||||||
|
and self.aparams['def_netType'] != "PRIVATE"
|
||||||
|
)
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "enable_default_gateway" failed: '
|
||||||
|
'cannot use param enable_default_gateway with a '
|
||||||
|
'non-private default network.'
|
||||||
|
)
|
||||||
|
check_errors = True
|
||||||
|
|
||||||
|
if self.rg_info.default_net_type == sdk_types.RGDefaultNetType.PRIVATE: # noqa: E501
|
||||||
|
def_net = self.api.cloudapi.vins.get(
|
||||||
|
vins_id=self.rg_info.default_net_id
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
def_net.enable_default_gateway
|
||||||
|
!= self.aparams['enable_default_gateway']
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "enable_default_gateway" '
|
||||||
|
'failed: cannot change enable_default_gateway for a '
|
||||||
|
f'default network ID {self.rg_info.default_net_id}.'
|
||||||
|
)
|
||||||
|
check_errors = True
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
def check_amodule_args_for_create(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['enable_default_gateway'] is None
|
||||||
|
and self.aparams['def_netType'] == "PRIVATE"
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "enable_default_gateway" failed: '
|
||||||
|
'enable_default_gateway is required when creating a '
|
||||||
|
'resource group with a private network.'
|
||||||
|
)
|
||||||
|
check_errors = True
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['enable_default_gateway'] is not None
|
||||||
|
and self.aparams['def_netType'] != "PRIVATE"
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "enable_default_gateway" failed: '
|
||||||
|
'cannot create resource group with enable_default_gateway '
|
||||||
|
'for a non-private network group ID.'
|
||||||
)
|
)
|
||||||
check_errors = True
|
check_errors = True
|
||||||
|
|
||||||
@@ -410,10 +509,15 @@ class decort_rg(DecortController):
|
|||||||
def run(self):
|
def run(self):
|
||||||
amodule = self.amodule
|
amodule = self.amodule
|
||||||
#amodule.check_mode=True
|
#amodule.check_mode=True
|
||||||
if self.validated_rg_id > 0:
|
if self.rg_id:
|
||||||
if self.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]:
|
if self.rg_info.status in [
|
||||||
|
sdk_types.ResourceGroupStatus.MODELED,
|
||||||
|
sdk_types.ResourceGroupStatus.DISABLING,
|
||||||
|
sdk_types.ResourceGroupStatus.ENABLING,
|
||||||
|
sdk_types.ResourceGroupStatus.DESTROYING,
|
||||||
|
]:
|
||||||
self.error()
|
self.error()
|
||||||
elif self.rg_facts['status'] in ("CREATED"):
|
elif self.rg_info.status == sdk_types.ResourceGroupStatus.CREATED:
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
self.destroy()
|
self.destroy()
|
||||||
elif amodule.params['state'] == "disabled":
|
elif amodule.params['state'] == "disabled":
|
||||||
@@ -432,7 +536,7 @@ class decort_rg(DecortController):
|
|||||||
if amodule.params['def_netType'] is not None:
|
if amodule.params['def_netType'] is not None:
|
||||||
self.setDefNet()
|
self.setDefNet()
|
||||||
|
|
||||||
elif self.rg_facts['status'] == "DELETED":
|
elif self.rg_info.status == sdk_types.ResourceGroupStatus.DELETED:
|
||||||
if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True:
|
if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True:
|
||||||
self.destroy()
|
self.destroy()
|
||||||
elif (amodule.params['state'] == 'present'
|
elif (amodule.params['state'] == 'present'
|
||||||
@@ -441,7 +545,7 @@ class decort_rg(DecortController):
|
|||||||
elif amodule.params['state'] == 'enabled':
|
elif amodule.params['state'] == 'enabled':
|
||||||
self.restore()
|
self.restore()
|
||||||
self.enable()
|
self.enable()
|
||||||
elif self.rg_facts['status'] in ("DISABLED"):
|
elif self.rg_info.status == sdk_types.ResourceGroupStatus.DISABLED:
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
self.destroy()
|
self.destroy()
|
||||||
elif amodule.params['state'] == ("enabled"):
|
elif amodule.params['state'] == ("enabled"):
|
||||||
@@ -456,7 +560,8 @@ class decort_rg(DecortController):
|
|||||||
' the "rg_name" parameter was not specified.'
|
' the "rg_name" parameter was not specified.'
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.create()
|
self.check_amodule_args_for_create()
|
||||||
|
self.create()
|
||||||
if amodule.params['access'] and not amodule.check_mode:
|
if amodule.params['access'] and not amodule.check_mode:
|
||||||
self.access()
|
self.access()
|
||||||
elif amodule.params['state'] in ('disabled'):
|
elif amodule.params['state'] in ('disabled'):
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class DecortRGList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
213
library/decort_sdn_access_group.py
Normal file
213
library/decort_sdn_access_group.py
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_access_group
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNAccessGroups(DecortController):
|
||||||
|
access_group_id: str | None = None
|
||||||
|
_access_group_info: dict[str, Any] | None = None
|
||||||
|
need_final_get: bool = True
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
access_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
comment=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['present', 'absent'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def access_group_info(self) -> dict[str, Any]:
|
||||||
|
if self._access_group_info is None:
|
||||||
|
if not isinstance(self.access_group_id, str):
|
||||||
|
raise TypeError
|
||||||
|
access_group_info = self.get()
|
||||||
|
if access_group_info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._access_group_info = access_group_info
|
||||||
|
return self._access_group_info
|
||||||
|
|
||||||
|
def check_amodule_args(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['state'] == 'absent'
|
||||||
|
and self.aparams['access_group_id'] is None
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "access_group_id" failed: '
|
||||||
|
'access_group_id must be specified when state is "absent".'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
def check_amodule_args_for_create(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if self.aparams['comment'] is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "comment" failed: parameter must '
|
||||||
|
'be specified for creating an access_group.'
|
||||||
|
)
|
||||||
|
if self.aparams['display_name'] is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "display_name" failed: parameter must '
|
||||||
|
'be specified for creating an access_group.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def get(self) -> dict[str, Any] | None:
|
||||||
|
params = {'access_group_id': self.access_group_id}
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/access_group/get',
|
||||||
|
arg_params=params,
|
||||||
|
not_fail_codes=[404],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='access_group',
|
||||||
|
id=self.access_group_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def access_group_find(self, display_name: str) -> dict[str, Any] | None:
|
||||||
|
params = {'display_name': display_name}
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/access_group/list',
|
||||||
|
arg_params=params,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
for access_group in response.json() or []:
|
||||||
|
if access_group['display_name'] == display_name:
|
||||||
|
return access_group
|
||||||
|
return None
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def create(self):
|
||||||
|
params = {
|
||||||
|
'comment': self.aparams['comment'],
|
||||||
|
'display_name': self.aparams['display_name'],
|
||||||
|
}
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.post,
|
||||||
|
arg_api_name='/restmachine/sdn/access_group/create',
|
||||||
|
arg_params=params,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.access_group_id = response.json()['id']
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def delete(self):
|
||||||
|
params = {'access_group_id': self.access_group_id}
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.delete,
|
||||||
|
arg_api_name='/restmachine/sdn/access_group/delete',
|
||||||
|
arg_params=params,
|
||||||
|
not_fail_codes=[204, 404]
|
||||||
|
)
|
||||||
|
self.need_final_get = False
|
||||||
|
if response.status_code == 204:
|
||||||
|
self.set_changed()
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_deleted(
|
||||||
|
obj='access_group',
|
||||||
|
id=self.access_group_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='access_group',
|
||||||
|
id=self.access_group_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.facts = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.check_amodule_args()
|
||||||
|
|
||||||
|
if self.aparams['access_group_id']:
|
||||||
|
self.access_group_id = self.aparams['access_group_id']
|
||||||
|
elif self.aparams['state'] != 'absent':
|
||||||
|
self.check_amodule_args_for_create()
|
||||||
|
access_group_info = self.access_group_find(
|
||||||
|
display_name=self.aparams['display_name'],
|
||||||
|
)
|
||||||
|
if access_group_info:
|
||||||
|
self.access_group_id = access_group_info['id']
|
||||||
|
self._access_group_info = access_group_info
|
||||||
|
|
||||||
|
if self.access_group_id:
|
||||||
|
if self.aparams['state'] == 'absent':
|
||||||
|
self.delete()
|
||||||
|
else:
|
||||||
|
state = self.aparams['state']
|
||||||
|
if state is None:
|
||||||
|
state = 'present'
|
||||||
|
self.message(
|
||||||
|
msg=self.MESSAGES.default_value_used(
|
||||||
|
param_name='state',
|
||||||
|
default_value=state,
|
||||||
|
),
|
||||||
|
warning=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if state != 'absent':
|
||||||
|
self.create()
|
||||||
|
|
||||||
|
if self.need_final_get:
|
||||||
|
self.facts = self.get()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNAccessGroups().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
120
library/decort_sdn_access_group_list.py
Normal file
120
library/decort_sdn_access_group_list.py
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_access_group_list
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNAccessGroupList(DecortController):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
filter=dict(
|
||||||
|
type='dict',
|
||||||
|
apply_defaults=True,
|
||||||
|
options=dict(
|
||||||
|
enabled=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
deleted=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
owner_display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
created_from=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
created_to=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pagination=dict(
|
||||||
|
type='dict',
|
||||||
|
apply_defaults=True,
|
||||||
|
options=dict(
|
||||||
|
number=dict(
|
||||||
|
type='int',
|
||||||
|
default=1,
|
||||||
|
),
|
||||||
|
size=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sorting=dict(
|
||||||
|
type='dict',
|
||||||
|
options=dict(
|
||||||
|
asc=dict(
|
||||||
|
type='bool',
|
||||||
|
default=True,
|
||||||
|
),
|
||||||
|
field=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'display_name',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
'deleted_at',
|
||||||
|
'owner_login',
|
||||||
|
],
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.get_info()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
params: dict[str, Any] = dict()
|
||||||
|
|
||||||
|
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||||
|
for field, value in aparam_filter.items():
|
||||||
|
if value is not None:
|
||||||
|
params[field] = value
|
||||||
|
|
||||||
|
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||||
|
params['page'] = aparam_pagination['number']
|
||||||
|
params['per_page'] = aparam_pagination['size']
|
||||||
|
|
||||||
|
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
|
||||||
|
if aparam_sorting:
|
||||||
|
params['sort_by'] = aparam_sorting['field']
|
||||||
|
params['sort_order'] = 'asc' if aparam_sorting['asc'] else 'desc'
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/access_group/list',
|
||||||
|
arg_params=params,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.facts = response.json()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNAccessGroupList().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
160
library/decort_sdn_hypervisor.py
Normal file
160
library/decort_sdn_hypervisor.py
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_hypervisor
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNHypervisor(DecortController):
|
||||||
|
name: str | None = None
|
||||||
|
_hypervisor_info: dict[str, Any] | None = None
|
||||||
|
need_final_get: bool = True
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
port_info=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['detailed', 'general'],
|
||||||
|
),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['absent'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hypervisor_info(self) -> dict[str, Any]:
|
||||||
|
if self._hypervisor_info is None:
|
||||||
|
if not isinstance(self.name, str):
|
||||||
|
raise TypeError
|
||||||
|
hypervisor_info = self.get()
|
||||||
|
if hypervisor_info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._hypervisor_info = hypervisor_info
|
||||||
|
return self._hypervisor_info
|
||||||
|
|
||||||
|
def check_amodule_args(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if self.aparams['name'] is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "name" failed: '
|
||||||
|
'name must be specified.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def get(self) -> dict[str, Any] | None:
|
||||||
|
params = {'name': self.name}
|
||||||
|
if self.aparams['port_info'] is not None:
|
||||||
|
params['port_info'] = self.aparams['port_info']
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/hypervisor/get',
|
||||||
|
arg_params=params,
|
||||||
|
not_fail_codes=[400],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 400:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='hypervisor',
|
||||||
|
id=self.name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def update_display_name(self):
|
||||||
|
params = {
|
||||||
|
'name': self.aparams['name'],
|
||||||
|
'display_name': self.aparams['display_name'],
|
||||||
|
}
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.put,
|
||||||
|
arg_api_name='/restmachine/sdn/hypervisor/update_display_name',
|
||||||
|
arg_params=params,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def delete(self):
|
||||||
|
params = {'name': self.name}
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.delete,
|
||||||
|
arg_api_name='/restmachine/sdn/hypervisor/delete',
|
||||||
|
arg_params=params,
|
||||||
|
not_fail_codes=[400]
|
||||||
|
)
|
||||||
|
self.need_final_get = False
|
||||||
|
if response.status_code == 200:
|
||||||
|
self.set_changed()
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_deleted(
|
||||||
|
obj='hypervisor',
|
||||||
|
id=self.name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='hypervisor',
|
||||||
|
id=self.name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.facts = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.check_amodule_args()
|
||||||
|
self.name = self.aparams['name']
|
||||||
|
|
||||||
|
if self.aparams['state'] == 'absent':
|
||||||
|
self.delete()
|
||||||
|
else:
|
||||||
|
if (
|
||||||
|
self.aparams['display_name'] is not None
|
||||||
|
and self.aparams['display_name']
|
||||||
|
!= self.hypervisor_info.get('display_name')
|
||||||
|
):
|
||||||
|
self.update_display_name()
|
||||||
|
|
||||||
|
if self.need_final_get:
|
||||||
|
self.facts = self.get()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNHypervisor().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
135
library/decort_sdn_hypervisor_list.py
Normal file
135
library/decort_sdn_hypervisor_list.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_hypervisor_list
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNHypervisorList(DecortController):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
port_info=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['detailed', 'general']
|
||||||
|
),
|
||||||
|
filter=dict(
|
||||||
|
type='dict',
|
||||||
|
apply_defaults=True,
|
||||||
|
options=dict(
|
||||||
|
hostname=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
ip=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
created_from=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
created_to=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
updated_from=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
updated_to=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pagination=dict(
|
||||||
|
type='dict',
|
||||||
|
apply_defaults=True,
|
||||||
|
options=dict(
|
||||||
|
number=dict(
|
||||||
|
type='int',
|
||||||
|
default=1,
|
||||||
|
),
|
||||||
|
size=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sorting=dict(
|
||||||
|
type='dict',
|
||||||
|
options=dict(
|
||||||
|
asc=dict(
|
||||||
|
type='bool',
|
||||||
|
default=True,
|
||||||
|
),
|
||||||
|
field=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'name',
|
||||||
|
'hostname',
|
||||||
|
'last_sync',
|
||||||
|
'display_name',
|
||||||
|
'ip',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
],
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.get_info()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
api_params: dict[str, Any] = dict()
|
||||||
|
|
||||||
|
aparam_port_info = self.aparams['port_info']
|
||||||
|
if aparam_port_info is not None:
|
||||||
|
api_params['port_info'] = aparam_port_info
|
||||||
|
|
||||||
|
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||||
|
for field, value in aparam_filter.items():
|
||||||
|
if value is not None:
|
||||||
|
api_params[field] = value
|
||||||
|
|
||||||
|
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||||
|
api_params['page'] = aparam_pagination['number']
|
||||||
|
api_params['per_page'] = aparam_pagination['size']
|
||||||
|
|
||||||
|
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
|
||||||
|
if aparam_sorting:
|
||||||
|
api_params['sort_by'] = aparam_sorting['field']
|
||||||
|
api_params['sort_order'] = (
|
||||||
|
'asc' if aparam_sorting['asc'] else 'desc'
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/hypervisor/list',
|
||||||
|
arg_params=api_params,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.facts = response.json()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNHypervisorList().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
589
library/decort_sdn_logical_port.py
Normal file
589
library/decort_sdn_logical_port.py
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_logical_port
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNLogicalPort(DecortController):
|
||||||
|
FIELDS_FOR_CREATE = (
|
||||||
|
'access_group_id',
|
||||||
|
'adapter_mac',
|
||||||
|
'description',
|
||||||
|
'display_name',
|
||||||
|
'hypervisor',
|
||||||
|
'labels',
|
||||||
|
'port_security',
|
||||||
|
'segment_id',
|
||||||
|
'unique_identifier',
|
||||||
|
)
|
||||||
|
|
||||||
|
FIELDS_FOR_UPDATE = (
|
||||||
|
'adapter_mac',
|
||||||
|
'description',
|
||||||
|
'display_name',
|
||||||
|
'hypervisor',
|
||||||
|
'labels',
|
||||||
|
'logical_port_id',
|
||||||
|
'port_security',
|
||||||
|
'segment_id',
|
||||||
|
'version_id',
|
||||||
|
)
|
||||||
|
|
||||||
|
UPDATE_FIELDS = (
|
||||||
|
'adapter_mac',
|
||||||
|
'description',
|
||||||
|
'display_name',
|
||||||
|
'hypervisor',
|
||||||
|
'labels',
|
||||||
|
'port_security',
|
||||||
|
'segment_id',
|
||||||
|
'no_addresses',
|
||||||
|
)
|
||||||
|
|
||||||
|
logical_port_id: str | None = None
|
||||||
|
_logical_port_info: dict[str, Any] | None = None
|
||||||
|
need_final_get: bool = True
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
no_migration=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
hypervisor_change_via_migration=dict(
|
||||||
|
type='bool',
|
||||||
|
default=False,
|
||||||
|
),
|
||||||
|
logical_port_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'present',
|
||||||
|
'enabled',
|
||||||
|
'disabled',
|
||||||
|
'absent',
|
||||||
|
'absent_force',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
access_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
adapter_mac=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
description=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
hypervisor=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
labels=dict(
|
||||||
|
type='dict',
|
||||||
|
),
|
||||||
|
no_addresses=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
port_security=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
segment_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
unique_identifier=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
version_id=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
mutually_exclusive=[
|
||||||
|
('unique_identifier', 'logical_port_id'),
|
||||||
|
('no_migration', 'hypervisor_change_via_migration'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.check_amodule_args()
|
||||||
|
state = self.aparams['state']
|
||||||
|
if self.aparams['unique_identifier'] is not None:
|
||||||
|
self._logical_port_info = (
|
||||||
|
self.logical_port_get_by_unique_identifier(
|
||||||
|
fail_if_not_found=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.logical_port_id = self._logical_port_info['id']
|
||||||
|
elif self.aparams['logical_port_id']:
|
||||||
|
self.logical_port_id = self.aparams['logical_port_id']
|
||||||
|
self._logical_port_info = self.logical_port_get(
|
||||||
|
fail_if_not_found=False,
|
||||||
|
)
|
||||||
|
elif self.aparams['display_name']:
|
||||||
|
self._logical_port_info = self.logical_port_find(
|
||||||
|
display_name=self.aparams['display_name']
|
||||||
|
)
|
||||||
|
if self._logical_port_info:
|
||||||
|
self.logical_port_id = self._logical_port_info['id']
|
||||||
|
|
||||||
|
if self._logical_port_info is not None:
|
||||||
|
if state in ('absent', 'absent_force'):
|
||||||
|
self.logical_port_delete(
|
||||||
|
force=state == 'absent_force',
|
||||||
|
)
|
||||||
|
elif self.aparams['no_migration']:
|
||||||
|
self.logical_port_migration_cancel()
|
||||||
|
elif self.aparams['hypervisor_change_via_migration']:
|
||||||
|
self.logical_port_migration_start()
|
||||||
|
else:
|
||||||
|
self.logical_port_update(
|
||||||
|
desired_enabled=(
|
||||||
|
True if state == 'enabled'
|
||||||
|
else False if state == 'disabled'
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if state == 'present':
|
||||||
|
self.check_amodule_args_for_create()
|
||||||
|
self.logical_port_create()
|
||||||
|
elif state in ('enabled', 'disabled'):
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "state" failed: state values '
|
||||||
|
'"enabled"/"disabled" can only be applied to an existing '
|
||||||
|
'logical port.'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
else:
|
||||||
|
self.need_final_get = False
|
||||||
|
self.facts = {}
|
||||||
|
|
||||||
|
if self.need_final_get and not self.amodule.check_mode:
|
||||||
|
if self._logical_port_info is not None:
|
||||||
|
self.facts = self.logical_port_info
|
||||||
|
else:
|
||||||
|
self.facts = self.logical_port_get()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def check_amodule_args(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['no_migration']
|
||||||
|
and self.aparams['state'] == 'absent'
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "no_migration" failed: '
|
||||||
|
'no_migration cannot be used when state is "absent".'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['hypervisor_change_via_migration']
|
||||||
|
and self.aparams['hypervisor'] is None
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameters '
|
||||||
|
'"hypervisor_change_via_migration/hypervisor" failed: '
|
||||||
|
'"hypervisor" must be specified when '
|
||||||
|
'"hypervisor_change_via_migration" is true.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['no_migration']
|
||||||
|
and self.aparams['hypervisor'] is not None
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameters "no_migration/hypervisor" failed: '
|
||||||
|
'"hypervisor" cannot be used when "no_migration" is true.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
def check_amodule_args_for_create(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
for field in (
|
||||||
|
'access_group_id',
|
||||||
|
'description',
|
||||||
|
'display_name',
|
||||||
|
'hypervisor',
|
||||||
|
'port_security',
|
||||||
|
'segment_id',
|
||||||
|
):
|
||||||
|
if self.aparams[field] is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
f'Check for parameter "{field}" failed: parameter '
|
||||||
|
f'"{field}" is required when creating a logical port.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logical_port_info(self) -> dict[str, Any]:
|
||||||
|
if self._logical_port_info is None:
|
||||||
|
if (
|
||||||
|
self.aparams['unique_identifier'] is None
|
||||||
|
and self.aparams['logical_port_id'] is None
|
||||||
|
):
|
||||||
|
raise TypeError
|
||||||
|
logical_port_info = self.logical_port_get()
|
||||||
|
if logical_port_info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._logical_port_info = logical_port_info
|
||||||
|
return self._logical_port_info
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def logical_port_get_by_unique_identifier(
|
||||||
|
self,
|
||||||
|
fail_if_not_found: bool = True,
|
||||||
|
) -> dict[str, Any] | None:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name=(
|
||||||
|
'/restmachine/sdn/logical_port/get_by_unique_identifier'
|
||||||
|
),
|
||||||
|
arg_params={
|
||||||
|
'unique_identifier': self.aparams['unique_identifier']
|
||||||
|
},
|
||||||
|
not_fail_codes=[404],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
if fail_if_not_found:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='logical port',
|
||||||
|
id=self.aparams['unique_identifier'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return None
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def logical_port_get(
|
||||||
|
self,
|
||||||
|
fail_if_not_found: bool = True,
|
||||||
|
) -> dict[str, Any] | None:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/get',
|
||||||
|
arg_params={'logical_port_id': self.logical_port_id},
|
||||||
|
accept_json_response=True,
|
||||||
|
not_fail_codes=[404],
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
if fail_if_not_found:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='logical port',
|
||||||
|
id=self.logical_port_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return None
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def logical_port_find(self, display_name: str) -> dict[str, Any] | None:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/list',
|
||||||
|
arg_params={'display_name': display_name},
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
return response.json()[0] if response.json() else None
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def logical_port_create(self):
|
||||||
|
payload = {}
|
||||||
|
for field in self.FIELDS_FOR_CREATE:
|
||||||
|
value = self.aparams[field]
|
||||||
|
if value is not None:
|
||||||
|
payload[field] = value
|
||||||
|
payload['enabled'] = True
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.post,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/create',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.logical_port_id = response.json()['id']
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def logical_port_delete(
|
||||||
|
self,
|
||||||
|
force: bool = False,
|
||||||
|
):
|
||||||
|
version_id = self.aparams['version_id']
|
||||||
|
if version_id is None and self.logical_port_info is not None:
|
||||||
|
version_id = self.logical_port_info['version_id']
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'logical_port_id': self.logical_port_id,
|
||||||
|
'version_id': version_id,
|
||||||
|
'force': force,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.delete,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/delete',
|
||||||
|
arg_json_body=payload,
|
||||||
|
)
|
||||||
|
self.need_final_get = False
|
||||||
|
self.facts = {}
|
||||||
|
self.set_changed()
|
||||||
|
self.message(
|
||||||
|
msg=self.MESSAGES.obj_deleted(
|
||||||
|
obj='logical port',
|
||||||
|
id=self.logical_port_id,
|
||||||
|
permanently=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def logical_port_update(
|
||||||
|
self,
|
||||||
|
desired_enabled: bool | None = None,
|
||||||
|
):
|
||||||
|
need_update = False
|
||||||
|
|
||||||
|
addresses_to_remove = []
|
||||||
|
for field in self.UPDATE_FIELDS:
|
||||||
|
value = self.aparams[field]
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
if self.aparams['no_addresses']:
|
||||||
|
bindings = self.logical_port_info['bindings']
|
||||||
|
if bindings and bindings.get('logical_port_addresses'):
|
||||||
|
for address in bindings['logical_port_addresses']:
|
||||||
|
addresses_to_remove.append(address['id'])
|
||||||
|
if addresses_to_remove:
|
||||||
|
need_update = True
|
||||||
|
|
||||||
|
if field == 'port_security':
|
||||||
|
current_value = self.logical_port_info['bindings'].get(
|
||||||
|
'port_security'
|
||||||
|
)
|
||||||
|
elif field == 'segment_id':
|
||||||
|
current_value = self.logical_port_info['bindings'].get(
|
||||||
|
'segment_id'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
current_value = self.logical_port_info.get(field)
|
||||||
|
if isinstance(value, dict) and isinstance(current_value, dict):
|
||||||
|
if any(
|
||||||
|
current_value.get(key) != expected_value
|
||||||
|
for key, expected_value in value.items()
|
||||||
|
):
|
||||||
|
need_update = True
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
if value != current_value:
|
||||||
|
need_update = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if (
|
||||||
|
not need_update
|
||||||
|
and desired_enabled is not None
|
||||||
|
and desired_enabled != self.logical_port_info['enabled']
|
||||||
|
):
|
||||||
|
need_update = True
|
||||||
|
|
||||||
|
if need_update:
|
||||||
|
payload = {
|
||||||
|
'logical_port_id': self.logical_port_id,
|
||||||
|
'version_id': (
|
||||||
|
self.aparams['version_id']
|
||||||
|
or self.logical_port_info['version_id']
|
||||||
|
),
|
||||||
|
'adapter_mac': (
|
||||||
|
self.aparams['adapter_mac']
|
||||||
|
or self.logical_port_info['adapter_mac']
|
||||||
|
),
|
||||||
|
'description': (
|
||||||
|
self.aparams['description']
|
||||||
|
or self.logical_port_info['description']
|
||||||
|
),
|
||||||
|
'display_name': (
|
||||||
|
self.aparams['display_name']
|
||||||
|
or self.logical_port_info['display_name']
|
||||||
|
),
|
||||||
|
'enabled': (
|
||||||
|
desired_enabled
|
||||||
|
if desired_enabled is not None
|
||||||
|
else self.logical_port_info['enabled']
|
||||||
|
),
|
||||||
|
'hypervisor': (
|
||||||
|
self.aparams['hypervisor']
|
||||||
|
or self.logical_port_info['hypervisor']
|
||||||
|
),
|
||||||
|
'labels': (
|
||||||
|
self.aparams['labels']
|
||||||
|
or self.logical_port_info.get('labels')
|
||||||
|
),
|
||||||
|
'port_security': (
|
||||||
|
self.aparams['port_security']
|
||||||
|
if self.aparams['port_security'] is not None
|
||||||
|
else self.logical_port_info['bindings']['port_security']
|
||||||
|
),
|
||||||
|
'segment_id': (
|
||||||
|
self.aparams['segment_id']
|
||||||
|
or self.logical_port_info['bindings']['segment_id']
|
||||||
|
),
|
||||||
|
}
|
||||||
|
if addresses_to_remove:
|
||||||
|
payload['remove_addresses'] = addresses_to_remove
|
||||||
|
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.put,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/update',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self._logical_port_info = None
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def logical_port_migration_cancel(self):
|
||||||
|
version_id = self.aparams['version_id']
|
||||||
|
if version_id is None and self._logical_port_info is not None:
|
||||||
|
version_id = self._logical_port_info['version_id']
|
||||||
|
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.delete,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/migration_cancel',
|
||||||
|
arg_json_body={
|
||||||
|
'logical_port_id': self.logical_port_id,
|
||||||
|
'version_id': version_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def logical_port_migration_start(self):
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.post,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/migration_start',
|
||||||
|
arg_json_body={
|
||||||
|
'logical_port_id': self.logical_port_id,
|
||||||
|
'target_hypervisor': self.aparams['hypervisor'],
|
||||||
|
'version_id': (
|
||||||
|
self.aparams['version_id']
|
||||||
|
or self.logical_port_info['version_id']
|
||||||
|
if self._logical_port_info is not None
|
||||||
|
else self.aparams['version_id']
|
||||||
|
),
|
||||||
|
},
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
waiting_statuses = {
|
||||||
|
'Idle',
|
||||||
|
'SynchronizingAtCore',
|
||||||
|
'SynchronizingAtOVN',
|
||||||
|
}
|
||||||
|
failed_statuses = {
|
||||||
|
'NoHypervisorAtOVN': (
|
||||||
|
'Logical port migration failed: no hypervisor at OVN.'
|
||||||
|
),
|
||||||
|
'FailedAtCore': 'Logical port migration failed at core.',
|
||||||
|
'TemporaryFailedAtCore': (
|
||||||
|
'Logical port migration temporary failed at core.'
|
||||||
|
),
|
||||||
|
}
|
||||||
|
target_hypervisor = self.aparams['hypervisor']
|
||||||
|
|
||||||
|
for _ in range(300):
|
||||||
|
logical_port_info = self.logical_port_get()
|
||||||
|
if logical_port_info is None:
|
||||||
|
self.message(
|
||||||
|
'Logical port migration failed: '
|
||||||
|
'can\'t get logical port info.'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
status_info = logical_port_info.get('status')
|
||||||
|
if not isinstance(status_info, dict):
|
||||||
|
status_info = {}
|
||||||
|
hypervisors = status_info.get('hypervisors', [])
|
||||||
|
|
||||||
|
target_hypervisor_status = None
|
||||||
|
for hypervisor in hypervisors:
|
||||||
|
if hypervisor.get('name') == target_hypervisor:
|
||||||
|
target_hypervisor_status = str(
|
||||||
|
hypervisor.get('operation_status')
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
if target_hypervisor_status == 'Synchronized':
|
||||||
|
self._logical_port_info = logical_port_info
|
||||||
|
self.set_changed()
|
||||||
|
return
|
||||||
|
|
||||||
|
if target_hypervisor_status == 'NoHypervisorAtOVN':
|
||||||
|
self.message(failed_statuses['NoHypervisorAtOVN'])
|
||||||
|
self.exit(fail=True)
|
||||||
|
if target_hypervisor_status == 'FailedAtCore':
|
||||||
|
self.message(failed_statuses['FailedAtCore'])
|
||||||
|
self.exit(fail=True)
|
||||||
|
if target_hypervisor_status == 'TemporaryFailedAtCore':
|
||||||
|
self.message(failed_statuses['TemporaryFailedAtCore'])
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
if target_hypervisor_status in waiting_statuses:
|
||||||
|
time.sleep(5)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if target_hypervisor_status is None:
|
||||||
|
self.message(
|
||||||
|
'Logical port migration failed: target hypervisor '
|
||||||
|
f'"{target_hypervisor}" not found in status.'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.message(
|
||||||
|
'Logical port migration failed with unexpected status '
|
||||||
|
f'for hypervisor "{target_hypervisor}": '
|
||||||
|
f'{target_hypervisor_status}.'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
self.message('Logical port migration timed out.')
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNLogicalPort().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
206
library/decort_sdn_logical_port_address.py
Normal file
206
library/decort_sdn_logical_port_address.py
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_logical_port_address
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNLogicalPortAddress(DecortController):
|
||||||
|
logical_port_id: str | None = None
|
||||||
|
_logical_port_info: dict[str, Any] | None = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
discovered=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
ip_addr=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
ip_type=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['IPv4', 'IPv6'],
|
||||||
|
),
|
||||||
|
logical_port_id=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
mac=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
primary=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['present', 'absent'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_amodule_args_for_create(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if self.aparams['ip_type'] is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "ip_type" failed: '
|
||||||
|
'ip_type must be specified when creating a new address.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.aparams['mac'] is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "mac" failed: '
|
||||||
|
'mac must be specified when creating a new address.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logical_port_info(self) -> dict[str, Any]:
|
||||||
|
if self._logical_port_info is None:
|
||||||
|
logical_port_info = self.logical_port_get()
|
||||||
|
if logical_port_info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._logical_port_info = logical_port_info
|
||||||
|
return self._logical_port_info
|
||||||
|
|
||||||
|
def find_address(self) -> dict[str, Any] | None:
|
||||||
|
addresses = self.logical_port_info.get('bindings', {}).get(
|
||||||
|
'logical_port_addresses', []
|
||||||
|
)
|
||||||
|
for addr in addresses:
|
||||||
|
if addr.get('ip') == self.aparams['ip_addr']:
|
||||||
|
return addr
|
||||||
|
return None
|
||||||
|
|
||||||
|
def port_payload(self) -> dict:
|
||||||
|
info = self.logical_port_info
|
||||||
|
return {
|
||||||
|
'logical_port_id': self.logical_port_id,
|
||||||
|
'version_id': info['version_id'],
|
||||||
|
'adapter_mac': info['adapter_mac'],
|
||||||
|
'description': info['description'],
|
||||||
|
'display_name': info['display_name'],
|
||||||
|
'enabled': info['enabled'],
|
||||||
|
'hypervisor': info['hypervisor'],
|
||||||
|
'labels': info.get('labels'),
|
||||||
|
'port_security': info['bindings']['port_security'],
|
||||||
|
'segment_id': info['bindings']['segment_id'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def logical_port_update(self, payload: dict):
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.put,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/update',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self._logical_port_info = response.json()
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def logical_port_get(self) -> dict[str, Any]:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/get',
|
||||||
|
arg_params={'logical_port_id': self.logical_port_id},
|
||||||
|
accept_json_response=True,
|
||||||
|
not_fail_codes=[404],
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='logical port',
|
||||||
|
id=self.logical_port_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def address_add(self):
|
||||||
|
payload = self.port_payload()
|
||||||
|
address_data: dict[str, Any] = {'ip': self.aparams['ip_addr']}
|
||||||
|
for param_name, api_param_name in (
|
||||||
|
('ip_type', 'ip_type'),
|
||||||
|
('discovered', 'is_discovered'),
|
||||||
|
('primary', 'is_primary'),
|
||||||
|
('mac', 'mac'),
|
||||||
|
):
|
||||||
|
if self.aparams[param_name] is not None:
|
||||||
|
address_data[api_param_name] = self.aparams[param_name]
|
||||||
|
payload['add_addresses'] = [address_data]
|
||||||
|
self.logical_port_update(payload)
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def address_remove(self, address_id: str):
|
||||||
|
payload = self.port_payload()
|
||||||
|
payload['remove_addresses'] = [address_id]
|
||||||
|
self.logical_port_update(payload)
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_deleted(
|
||||||
|
obj='logical port address',
|
||||||
|
id=self.aparams['ip_addr'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.logical_port_id = self.aparams['logical_port_id']
|
||||||
|
|
||||||
|
existing_addr = self.find_address()
|
||||||
|
|
||||||
|
if existing_addr:
|
||||||
|
if self.aparams['state'] == 'absent':
|
||||||
|
self.address_remove(existing_addr['id'])
|
||||||
|
else:
|
||||||
|
state = self.aparams['state']
|
||||||
|
if state is None:
|
||||||
|
state = 'present'
|
||||||
|
self.message(
|
||||||
|
msg=self.MESSAGES.default_value_used(
|
||||||
|
param_name='state',
|
||||||
|
default_value=state,
|
||||||
|
),
|
||||||
|
warning=True,
|
||||||
|
)
|
||||||
|
if state == 'absent':
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='logical port address',
|
||||||
|
id=self.aparams['ip_addr'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.check_amodule_args_for_create()
|
||||||
|
self.address_add()
|
||||||
|
|
||||||
|
self.facts = self.find_address() or {}
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNLogicalPortAddress().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
171
library/decort_sdn_logical_port_list.py
Normal file
171
library/decort_sdn_logical_port_list.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_logical_port_list
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNLogicalPortList(DecortController):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
filter=dict(
|
||||||
|
type='dict',
|
||||||
|
apply_defaults=True,
|
||||||
|
options=dict(
|
||||||
|
access_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
adapter_mac=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
address_detection=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
created_from=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
created_to=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
enabled=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
external_network_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
hypervisor=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
hypervisor_display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
hypervisor_status=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['Up', 'Warning', 'Error'],
|
||||||
|
),
|
||||||
|
live_migration_target_hv=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
operation_status=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'Idle',
|
||||||
|
'SynchronizingAtCore',
|
||||||
|
'SynchronizingAtOVN',
|
||||||
|
'Synchronized',
|
||||||
|
'NoHypervisorAtOVN',
|
||||||
|
'FailedAtCore',
|
||||||
|
'TemporaryFailedAtCore',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
port_security=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
segment_display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
segment_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
unique_identifier=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pagination=dict(
|
||||||
|
type='dict',
|
||||||
|
apply_defaults=True,
|
||||||
|
options=dict(
|
||||||
|
number=dict(
|
||||||
|
type='int',
|
||||||
|
default=1,
|
||||||
|
),
|
||||||
|
size=dict(
|
||||||
|
type='int',
|
||||||
|
default=50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sorting=dict(
|
||||||
|
type='dict',
|
||||||
|
options=dict(
|
||||||
|
asc=dict(
|
||||||
|
type='bool',
|
||||||
|
default=True,
|
||||||
|
),
|
||||||
|
field=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'created_at',
|
||||||
|
'deleted_at',
|
||||||
|
'display_name',
|
||||||
|
'hypervisor',
|
||||||
|
'hypervisor_display_name',
|
||||||
|
'port_security',
|
||||||
|
'primary_address',
|
||||||
|
'segment_display_name',
|
||||||
|
'segment_id',
|
||||||
|
'updated_at',
|
||||||
|
],
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.get_info()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
api_params: dict[str, Any] = dict()
|
||||||
|
|
||||||
|
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||||
|
for field, value in aparam_filter.items():
|
||||||
|
if value is not None:
|
||||||
|
api_params[field] = value
|
||||||
|
|
||||||
|
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||||
|
api_params['page'] = aparam_pagination['number']
|
||||||
|
api_params['per_page'] = aparam_pagination['size']
|
||||||
|
|
||||||
|
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
|
||||||
|
if aparam_sorting:
|
||||||
|
api_params['sort_by'] = aparam_sorting['field']
|
||||||
|
api_params['sort_order'] = (
|
||||||
|
'asc' if aparam_sorting['asc'] else 'desc'
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/list',
|
||||||
|
arg_params=api_params,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.facts = response.json()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNLogicalPortList().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
299
library/decort_sdn_network_object_group.py
Normal file
299
library/decort_sdn_network_object_group.py
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_network_object_group
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNNetworkObjectGroup(DecortController):
|
||||||
|
REQUIRED_FIELDS = (
|
||||||
|
'access_group_id',
|
||||||
|
'description',
|
||||||
|
'name',
|
||||||
|
)
|
||||||
|
|
||||||
|
object_group_id: str | None = None
|
||||||
|
_object_group_info: dict[str, Any] | None = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
access_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
description=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
no_addresses=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
no_logical_ports=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
object_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
version_id=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def object_group_info(self) -> dict[str, Any]:
|
||||||
|
if self._object_group_info is None:
|
||||||
|
if not isinstance(self.object_group_id, str):
|
||||||
|
raise TypeError
|
||||||
|
object_group_info = self.network_object_group_get()
|
||||||
|
if object_group_info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._object_group_info = object_group_info
|
||||||
|
return self._object_group_info
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.aparams['object_group_id']:
|
||||||
|
self.object_group_id = self.aparams['object_group_id']
|
||||||
|
elif self.aparams['name']:
|
||||||
|
object_group_info = self.network_object_group_find(
|
||||||
|
name=self.aparams['name']
|
||||||
|
)
|
||||||
|
if object_group_info:
|
||||||
|
self.object_group_id = object_group_info['id']
|
||||||
|
|
||||||
|
if self.object_group_id is None:
|
||||||
|
self.check_amodule_args_for_create()
|
||||||
|
self.network_object_group_create()
|
||||||
|
else:
|
||||||
|
self.check_amodule_args_for_update()
|
||||||
|
self.network_object_group_update()
|
||||||
|
|
||||||
|
if self.aparams['no_logical_ports']:
|
||||||
|
self.network_object_group_detach_logical_ports()
|
||||||
|
|
||||||
|
self.facts = self.network_object_group_get()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def check_amodule_args_for_create(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
for field in self.REQUIRED_FIELDS:
|
||||||
|
if self.aparams[field] is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
f'Check for parameter "{field}" failed: '
|
||||||
|
f'"{field}" is required when creating an object group.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
def check_amodule_args_for_update(self):
|
||||||
|
check_errors = False
|
||||||
|
version_id = self.aparams['version_id']
|
||||||
|
if (
|
||||||
|
version_id is not None
|
||||||
|
and version_id != self.object_group_info['version_id']
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameters "version_id" failed: '
|
||||||
|
'object group version mismatch: '
|
||||||
|
f'given version: {version_id}, '
|
||||||
|
f'current version: {self.object_group_info["version_id"]}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def network_object_group_get(self) -> dict[str, Any] | None:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/get',
|
||||||
|
arg_params={'object_group_id': self.object_group_id},
|
||||||
|
not_fail_codes=[404],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
self.message(
|
||||||
|
f'Network object group with id "{self.object_group_id}" '
|
||||||
|
'not found.'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def network_object_group_find(self, name: str) -> dict[str, Any] | None:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/list',
|
||||||
|
arg_params={'name': name},
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
return response.json()[0] if response.json() else None
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def network_object_group_create(self):
|
||||||
|
payload = dict()
|
||||||
|
for field in self.REQUIRED_FIELDS:
|
||||||
|
value = self.aparams[field]
|
||||||
|
if value is not None:
|
||||||
|
payload[field] = value
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.post,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/create',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self._object_group_info = response.json()
|
||||||
|
self.object_group_id = response.json()['id']
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def network_object_group_update(self):
|
||||||
|
need_update = False
|
||||||
|
|
||||||
|
remove_addresses = False
|
||||||
|
if self.aparams['no_addresses']:
|
||||||
|
current_addresses = self.object_group_info.get('addresses')
|
||||||
|
if isinstance(current_addresses, list) and current_addresses:
|
||||||
|
remove_addresses = True
|
||||||
|
need_update = True
|
||||||
|
|
||||||
|
for field in self.REQUIRED_FIELDS:
|
||||||
|
value = self.aparams[field]
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
current_value = self.object_group_info.get(field)
|
||||||
|
if isinstance(value, list) and isinstance(current_value, list):
|
||||||
|
if len(value) != len(current_value):
|
||||||
|
need_update = True
|
||||||
|
break
|
||||||
|
for index, expected_item in enumerate(value):
|
||||||
|
current_item = current_value[index]
|
||||||
|
if (
|
||||||
|
isinstance(expected_item, dict)
|
||||||
|
and isinstance(current_item, dict)
|
||||||
|
):
|
||||||
|
if any(
|
||||||
|
current_item.get(key) != expected_value
|
||||||
|
for key, expected_value in expected_item.items()
|
||||||
|
):
|
||||||
|
need_update = True
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
if expected_item != current_item:
|
||||||
|
need_update = True
|
||||||
|
break
|
||||||
|
if need_update:
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
if value != current_value:
|
||||||
|
need_update = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not need_update:
|
||||||
|
return
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'object_group_id': self.object_group_id,
|
||||||
|
'version_id': (
|
||||||
|
self.aparams['version_id']
|
||||||
|
or self.object_group_info['version_id']
|
||||||
|
),
|
||||||
|
'access_group_id': (
|
||||||
|
self.aparams['access_group_id']
|
||||||
|
or self.object_group_info['access_group_id']
|
||||||
|
),
|
||||||
|
'description': (
|
||||||
|
self.aparams['description']
|
||||||
|
or self.object_group_info['description']
|
||||||
|
),
|
||||||
|
'name': (
|
||||||
|
self.aparams['name']
|
||||||
|
or self.object_group_info['name']
|
||||||
|
),
|
||||||
|
}
|
||||||
|
if remove_addresses:
|
||||||
|
payload['addresses'] = []
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.put,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/update',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self._object_group_info = response.json()
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def network_object_group_detach_logical_ports(self):
|
||||||
|
logical_ports = self.object_group_info.get('logical_ports')
|
||||||
|
if not isinstance(logical_ports, list) or not logical_ports:
|
||||||
|
return
|
||||||
|
|
||||||
|
port_bindings = []
|
||||||
|
for logical_port in logical_ports:
|
||||||
|
if (
|
||||||
|
isinstance(logical_port, dict)
|
||||||
|
and logical_port.get('id') is not None
|
||||||
|
and logical_port.get('version_id') is not None
|
||||||
|
):
|
||||||
|
port_bindings.append(
|
||||||
|
{
|
||||||
|
'port_id': logical_port['id'],
|
||||||
|
'port_version': logical_port['version_id'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if not port_bindings:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.post,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/detach_logical_ports', # noqa: E501
|
||||||
|
arg_json_body={
|
||||||
|
'access_group_id': (
|
||||||
|
self.aparams['access_group_id']
|
||||||
|
or self.object_group_info['access_group_id']
|
||||||
|
),
|
||||||
|
'object_group_id': self.object_group_id,
|
||||||
|
'version_id': (
|
||||||
|
self.aparams['version_id']
|
||||||
|
or self.object_group_info['version_id']
|
||||||
|
),
|
||||||
|
'port_bindings': port_bindings,
|
||||||
|
},
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self._object_group_info = None
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNNetworkObjectGroup().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
209
library/decort_sdn_network_object_group_ip_range.py
Normal file
209
library/decort_sdn_network_object_group_ip_range.py
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_network_object_group_ip_range
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNNetworkObjectGroupIPRange(DecortController):
|
||||||
|
object_group_id: str | None = None
|
||||||
|
_object_group_info: dict[str, Any] | None = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
object_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
ip_addr_range=dict(
|
||||||
|
type='dict',
|
||||||
|
required=True,
|
||||||
|
options=dict(
|
||||||
|
start=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
end=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ip_proto=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['IPv4', 'IPv6'],
|
||||||
|
),
|
||||||
|
net_prefix=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
choices=['present', 'absent'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
required_if=[('state', 'present', ['ip_proto'])],
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def object_group_info(self) -> dict[str, Any]:
|
||||||
|
if self._object_group_info is None:
|
||||||
|
object_group_info = self.network_object_group_get()
|
||||||
|
if object_group_info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._object_group_info = object_group_info
|
||||||
|
return self._object_group_info
|
||||||
|
|
||||||
|
def find_ip_range(self) -> dict | None:
|
||||||
|
ip_addr = self.aparams['ip_addr_range']['start']
|
||||||
|
ip_proto = self.aparams['ip_proto']
|
||||||
|
for addr in self.object_group_info.get('addresses') or []:
|
||||||
|
if addr.get('ip_addr') != ip_addr:
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
ip_proto is not None
|
||||||
|
and addr.get('net_address_type') != ip_proto
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
return addr
|
||||||
|
return None
|
||||||
|
|
||||||
|
def group_payload(self) -> dict:
|
||||||
|
return {
|
||||||
|
'object_group_id': self.object_group_id,
|
||||||
|
'version_id': self.object_group_info['version_id'],
|
||||||
|
'access_group_id': self.object_group_info['access_group_id'],
|
||||||
|
'description': self.object_group_info['description'],
|
||||||
|
'name': self.object_group_info['name'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def ip_range_add(self):
|
||||||
|
current_addresses = self.object_group_info.get('addresses') or []
|
||||||
|
ip_addr_range = self.aparams['ip_addr_range']
|
||||||
|
ip_proto = self.aparams['ip_proto']
|
||||||
|
|
||||||
|
ip_range_data = {'ip_addr': ip_addr_range['start']}
|
||||||
|
if ip_proto is not None:
|
||||||
|
ip_range_data['net_address_type'] = ip_proto
|
||||||
|
if ip_addr_range['end'] is not None:
|
||||||
|
ip_range_data['ip_addr_range_end'] = ip_addr_range['end']
|
||||||
|
if self.aparams['net_prefix'] is not None:
|
||||||
|
ip_range_data['ip_prefix'] = self.aparams['net_prefix']
|
||||||
|
|
||||||
|
for index, addr in enumerate(current_addresses):
|
||||||
|
if addr.get('ip_addr') != ip_range_data['ip_addr']:
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
ip_proto is not None
|
||||||
|
and addr.get('net_address_type') != ip_proto
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
if not any(
|
||||||
|
addr.get(field) != value
|
||||||
|
for field, value in ip_range_data.items()
|
||||||
|
if field in addr
|
||||||
|
):
|
||||||
|
return
|
||||||
|
updated_addresses = list(current_addresses)
|
||||||
|
updated_addresses[index] = {**addr, **ip_range_data}
|
||||||
|
self.network_object_group_update_addresses(updated_addresses)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.network_object_group_update_addresses(
|
||||||
|
list(current_addresses) + [ip_range_data]
|
||||||
|
)
|
||||||
|
|
||||||
|
def ip_range_remove(self):
|
||||||
|
current_addresses = self.object_group_info.get('addresses') or []
|
||||||
|
ip_addr = self.aparams['ip_addr_range']['start']
|
||||||
|
ip_proto = self.aparams['ip_proto']
|
||||||
|
|
||||||
|
addresses_to_keep = [
|
||||||
|
addr for addr in current_addresses
|
||||||
|
if addr.get('ip_addr') != ip_addr
|
||||||
|
or (
|
||||||
|
ip_proto is not None
|
||||||
|
and addr.get('net_address_type') != ip_proto
|
||||||
|
)
|
||||||
|
]
|
||||||
|
if len(addresses_to_keep) == len(current_addresses):
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='ip_range',
|
||||||
|
id=ip_addr,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.network_object_group_update_addresses(addresses_to_keep)
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_deleted(
|
||||||
|
obj='ip_range',
|
||||||
|
id=ip_addr,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def network_object_group_get(self) -> dict[str, Any]:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/get',
|
||||||
|
arg_params={'object_group_id': self.object_group_id},
|
||||||
|
not_fail_codes=[404],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='network object group',
|
||||||
|
id=self.object_group_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def network_object_group_update_addresses(self, addresses: list):
|
||||||
|
payload = self.group_payload()
|
||||||
|
payload['addresses'] = addresses
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.put,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/update',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self._object_group_info = response.json()
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.object_group_id = self.aparams['object_group_id']
|
||||||
|
|
||||||
|
if self.aparams['state'] == 'present':
|
||||||
|
self.ip_range_add()
|
||||||
|
else:
|
||||||
|
self.ip_range_remove()
|
||||||
|
|
||||||
|
self.facts = self.find_ip_range() or {}
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNNetworkObjectGroupIPRange().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
245
library/decort_sdn_network_object_group_logical_port.py
Normal file
245
library/decort_sdn_network_object_group_logical_port.py
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_network_object_group_logical_port
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNNetworkObjectGroupLogicalPort(DecortController):
|
||||||
|
object_group_id: str
|
||||||
|
_object_group_info: dict[str, Any] | None = None
|
||||||
|
object_group_version: int | None = None
|
||||||
|
logical_port_id: str
|
||||||
|
logical_port_version: int | None = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
access_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
object_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
logical_port_id=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
logical_port_version=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
|
version_id=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['present', 'absent'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def object_group_info(self) -> dict[str, Any]:
|
||||||
|
if self._object_group_info is None:
|
||||||
|
if not isinstance(self.object_group_id, str):
|
||||||
|
raise TypeError
|
||||||
|
info = self.network_object_group_get()
|
||||||
|
if info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._object_group_info = info
|
||||||
|
return self._object_group_info
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.object_group_id = self.aparams['object_group_id']
|
||||||
|
self.logical_port_id = self.aparams['logical_port_id']
|
||||||
|
|
||||||
|
is_attached = self.is_logical_port_attached()
|
||||||
|
state = self.aparams['state']
|
||||||
|
self.object_group_version = self.get_object_group_version()
|
||||||
|
self.logical_port_version = self.get_port_version(
|
||||||
|
is_attached=is_attached
|
||||||
|
)
|
||||||
|
|
||||||
|
if state == 'present':
|
||||||
|
if not is_attached:
|
||||||
|
self.network_object_group_logical_port_attach()
|
||||||
|
elif state == 'absent':
|
||||||
|
if is_attached:
|
||||||
|
self.network_object_group_logical_port_detach()
|
||||||
|
|
||||||
|
self.facts = {}
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_object_group_version(self) -> int:
|
||||||
|
provided_version: int | None = self.aparams['version_id']
|
||||||
|
current_version: int = self.object_group_info['version_id']
|
||||||
|
|
||||||
|
if (
|
||||||
|
provided_version is not None
|
||||||
|
and provided_version != current_version
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
'Check for parameters "version_id" failed: '
|
||||||
|
'object group version mismatch: '
|
||||||
|
f'given version: {provided_version}, '
|
||||||
|
f'current version: {current_version}.'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
return provided_version or current_version
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def network_object_group_get(self) -> dict[str, Any] | None:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/get',
|
||||||
|
arg_params={'object_group_id': self.object_group_id},
|
||||||
|
not_fail_codes=[404],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='network object group',
|
||||||
|
id=self.object_group_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def logical_port_get(self, logical_port_id: str) -> dict[str, Any]:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/logical_port/get',
|
||||||
|
arg_params={'logical_port_id': logical_port_id},
|
||||||
|
not_fail_codes=[404],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='logical port',
|
||||||
|
id=logical_port_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def is_logical_port_attached(self) -> bool:
|
||||||
|
logical_ports = self.object_group_info.get('logical_ports') or []
|
||||||
|
for lp in logical_ports:
|
||||||
|
if lp['id'] == self.logical_port_id:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_port_version_from_object_group(self) -> int | None:
|
||||||
|
logical_ports = self.object_group_info.get('logical_ports') or []
|
||||||
|
for lp in logical_ports:
|
||||||
|
if lp['id'] != self.logical_port_id:
|
||||||
|
continue
|
||||||
|
return lp['version_id']
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_port_version(self, is_attached: bool) -> int:
|
||||||
|
provided_version = self.aparams['logical_port_version']
|
||||||
|
|
||||||
|
current_version = None
|
||||||
|
if is_attached:
|
||||||
|
current_version = self.get_port_version_from_object_group()
|
||||||
|
|
||||||
|
if current_version is None:
|
||||||
|
info = self.logical_port_get(logical_port_id=self.logical_port_id)
|
||||||
|
current_version = info['version_id']
|
||||||
|
|
||||||
|
if (
|
||||||
|
provided_version is not None
|
||||||
|
and provided_version != current_version
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "logical_port_version" failed: '
|
||||||
|
'logical port version mismatch: '
|
||||||
|
f'given version: {provided_version}, '
|
||||||
|
f'current version: {current_version}.'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
return provided_version or current_version
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def network_object_group_logical_port_attach(self):
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.post,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/attach_logical_ports', # noqa: E501
|
||||||
|
arg_json_body={
|
||||||
|
'object_group_id': self.object_group_id,
|
||||||
|
'access_group_id': self.aparams['access_group_id'],
|
||||||
|
'version_id': self.object_group_version,
|
||||||
|
'port_bindings': [
|
||||||
|
{
|
||||||
|
'port_id': self.logical_port_id,
|
||||||
|
'port_version': self.logical_port_version,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.message(
|
||||||
|
msg=(
|
||||||
|
f'Logical port ID {self.logical_port_id} attached to object '
|
||||||
|
f'group ID {self.object_group_id}.'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def network_object_group_logical_port_detach(self):
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.post,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/detach_logical_ports', # noqa: E501
|
||||||
|
arg_json_body={
|
||||||
|
'object_group_id': self.object_group_id,
|
||||||
|
'access_group_id': self.aparams['access_group_id'],
|
||||||
|
'version_id': self.object_group_version,
|
||||||
|
'port_bindings': [
|
||||||
|
{
|
||||||
|
'port_id': self.logical_port_id,
|
||||||
|
'port_version': self.logical_port_version,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.message(
|
||||||
|
msg=(
|
||||||
|
f'Logical port ID {self.logical_port_id} detached from object '
|
||||||
|
f'group ID {self.object_group_id}.'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNNetworkObjectGroupLogicalPort().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
166
library/decort_sdn_network_object_group_mac.py
Normal file
166
library/decort_sdn_network_object_group_mac.py
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_network_object_group_mac
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNNetworkObjectGroupMAC(DecortController):
|
||||||
|
network_object_group_id: str | None = None
|
||||||
|
_network_object_group_info: dict[str, Any] | None = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
network_object_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
mac=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
required=True,
|
||||||
|
choices=['present', 'absent'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def network_network_object_group_info(self) -> dict[str, Any]:
|
||||||
|
if self._network_object_group_info is None:
|
||||||
|
network_network_object_group_info = self.network_object_group_get()
|
||||||
|
if network_network_object_group_info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._network_object_group_info = network_network_object_group_info
|
||||||
|
return self._network_object_group_info
|
||||||
|
|
||||||
|
def find_mac(self) -> dict | None:
|
||||||
|
mac = self.aparams['mac']
|
||||||
|
for addr in self.network_network_object_group_info.get('addresses') or []: # noqa: E501
|
||||||
|
if addr.get('mac_addr') == mac:
|
||||||
|
return addr
|
||||||
|
return None
|
||||||
|
|
||||||
|
def group_payload(self) -> dict:
|
||||||
|
return {
|
||||||
|
'object_group_id': self.network_object_group_id,
|
||||||
|
'version_id': self.network_network_object_group_info['version_id'],
|
||||||
|
'access_group_id': self.network_network_object_group_info['access_group_id'], # noqa: E501
|
||||||
|
'description': self.network_network_object_group_info['description'], # noqa: E501
|
||||||
|
'name': self.network_network_object_group_info['name'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def mac_add(self):
|
||||||
|
current_addresses = self.network_network_object_group_info.get('addresses') or [] # noqa: E501
|
||||||
|
mac = self.aparams['mac']
|
||||||
|
|
||||||
|
for addr in current_addresses:
|
||||||
|
if addr.get('mac_addr') == mac:
|
||||||
|
return
|
||||||
|
|
||||||
|
mac_data = {
|
||||||
|
'mac_addr': mac,
|
||||||
|
'net_address_type': 'MAC',
|
||||||
|
}
|
||||||
|
self.network_object_group_update_addresses(
|
||||||
|
list(current_addresses) + [mac_data]
|
||||||
|
)
|
||||||
|
|
||||||
|
def mac_remove(self):
|
||||||
|
current_addresses = self.network_network_object_group_info.get('addresses') or [] # noqa: E501
|
||||||
|
mac = self.aparams['mac']
|
||||||
|
|
||||||
|
addresses_to_keep = [
|
||||||
|
addr for addr in current_addresses
|
||||||
|
if addr.get('mac_addr') != mac
|
||||||
|
]
|
||||||
|
if len(addresses_to_keep) == len(current_addresses):
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='mac',
|
||||||
|
id=mac,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.network_object_group_update_addresses(addresses_to_keep)
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_deleted(
|
||||||
|
obj='mac',
|
||||||
|
id=mac,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
def network_object_group_get(self) -> dict[str, Any]:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/get',
|
||||||
|
arg_params={'object_group_id': self.network_object_group_id},
|
||||||
|
not_fail_codes=[404],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='network object group',
|
||||||
|
id=self.network_object_group_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def network_object_group_update_addresses(self, addresses: list):
|
||||||
|
payload = self.group_payload()
|
||||||
|
payload['addresses'] = addresses
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.put,
|
||||||
|
arg_api_name='/restmachine/sdn/network_object_group/update',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self._network_object_group_info = response.json()
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
def package_facts(self) -> dict:
|
||||||
|
facts = self.find_mac() or {}
|
||||||
|
if 'mac_addr' in facts:
|
||||||
|
facts['mac'] = facts.pop('mac_addr')
|
||||||
|
return facts
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.network_object_group_id = self.aparams['network_object_group_id']
|
||||||
|
|
||||||
|
if self.aparams['state'] == 'present':
|
||||||
|
self.mac_add()
|
||||||
|
else:
|
||||||
|
self.mac_remove()
|
||||||
|
|
||||||
|
self.facts = self.package_facts()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNNetworkObjectGroupMAC().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
357
library/decort_sdn_segment.py
Normal file
357
library/decort_sdn_segment.py
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_segment
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNSegment(DecortController):
|
||||||
|
REQUIRED_FIELDS = (
|
||||||
|
'access_group_id',
|
||||||
|
'description',
|
||||||
|
'dhcp_v4',
|
||||||
|
'dhcp_v6',
|
||||||
|
'display_name',
|
||||||
|
'subnet_v4',
|
||||||
|
'subnet_v6',
|
||||||
|
'type',
|
||||||
|
)
|
||||||
|
|
||||||
|
segment_id: str | None = None
|
||||||
|
_segment_info: dict[str, Any] | None = None
|
||||||
|
need_final_get: bool = True
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
access_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
description=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
dhcp_v4=dict(
|
||||||
|
type='dict',
|
||||||
|
),
|
||||||
|
dhcp_v6=dict(
|
||||||
|
type='dict',
|
||||||
|
),
|
||||||
|
display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
segment_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'present',
|
||||||
|
'enabled',
|
||||||
|
'disabled',
|
||||||
|
'absent',
|
||||||
|
'absent_force',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subnet_v4=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
subnet_v6=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
type=dict(
|
||||||
|
type='str',
|
||||||
|
choices=['User', 'ExtNet'],
|
||||||
|
),
|
||||||
|
version_id=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def segment_info(self) -> dict[str, Any]:
|
||||||
|
if self._segment_info is None:
|
||||||
|
if not isinstance(self.segment_id, str):
|
||||||
|
raise TypeError
|
||||||
|
segment_info = self.segment_get()
|
||||||
|
if segment_info is None:
|
||||||
|
raise TypeError
|
||||||
|
self._segment_info = segment_info
|
||||||
|
return self._segment_info
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
state = self.aparams['state']
|
||||||
|
|
||||||
|
if self.aparams['segment_id']:
|
||||||
|
self.segment_id = self.aparams['segment_id']
|
||||||
|
self._segment_info = self.segment_get(fail_if_not_found=False)
|
||||||
|
elif self.aparams['display_name']:
|
||||||
|
segment_info = self.segment_find(
|
||||||
|
display_name=self.aparams['display_name']
|
||||||
|
)
|
||||||
|
self._segment_info = segment_info
|
||||||
|
if segment_info:
|
||||||
|
self.segment_id = segment_info['id']
|
||||||
|
|
||||||
|
if state in ('absent', 'absent_force'):
|
||||||
|
if self._segment_info is None:
|
||||||
|
self.exit()
|
||||||
|
if self.segment_id:
|
||||||
|
self.segment_delete(
|
||||||
|
force=state == 'absent_force',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.need_final_get = False
|
||||||
|
elif state == 'present':
|
||||||
|
if self.segment_id:
|
||||||
|
self.check_amodule_args_for_update()
|
||||||
|
self.segment_update()
|
||||||
|
else:
|
||||||
|
self.check_amodule_args_for_create()
|
||||||
|
self.segment_create()
|
||||||
|
elif state in ('enabled', 'disabled'):
|
||||||
|
if not self.segment_id:
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "state" failed: state values '
|
||||||
|
'"enabled"/"disabled" can only be applied to an existing '
|
||||||
|
'segment.'
|
||||||
|
)
|
||||||
|
self.check_amodule_args_for_update()
|
||||||
|
self.segment_update(
|
||||||
|
desired_enabled=(state == 'enabled'),
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.need_final_get:
|
||||||
|
self.facts = self.segment_get()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def check_amodule_args_for_create(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['subnet_v4'] is None
|
||||||
|
and self.aparams['subnet_v6'] is None
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameters "subnet_v4/subnet_v6" failed: at '
|
||||||
|
'least one of these parameters must be specified when '
|
||||||
|
'creating a segment.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
def check_amodule_args_for_update(self):
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['version_id'] is not None
|
||||||
|
and self.aparams['version_id'] != self.segment_info['version_id']
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameters "version_id" failed: '
|
||||||
|
'segment version mismatch: '
|
||||||
|
f'given version: {self.aparams['version_id']}, '
|
||||||
|
f'current version: {self.segment_info['version_id']}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def segment_get(
|
||||||
|
self,
|
||||||
|
access_group_id: str | None = None,
|
||||||
|
fail_if_not_found=True,
|
||||||
|
) -> dict[str, Any] | None:
|
||||||
|
params = {'segment_id': self.segment_id}
|
||||||
|
if access_group_id is not None:
|
||||||
|
params['access_group_id'] = access_group_id
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/segment/get',
|
||||||
|
arg_params=params,
|
||||||
|
not_fail_codes=[404],
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
if fail_if_not_found:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='segment',
|
||||||
|
id=self.segment_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def segment_find(self, display_name: str) -> dict[str, Any] | None:
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/segment/list',
|
||||||
|
arg_params={
|
||||||
|
'display_name': display_name,
|
||||||
|
},
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
return response.json()[0] if response.json() else None
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def segment_create(self):
|
||||||
|
payload = dict()
|
||||||
|
for field in self.REQUIRED_FIELDS:
|
||||||
|
value = self.aparams[field]
|
||||||
|
if value is not None:
|
||||||
|
payload[field] = value
|
||||||
|
payload['enabled'] = True
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.post,
|
||||||
|
arg_api_name='/restmachine/sdn/segment/create',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self._segment_info = response.json()
|
||||||
|
self.segment_id = response.json()['id']
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def segment_update(
|
||||||
|
self,
|
||||||
|
desired_enabled: bool | None = None,
|
||||||
|
):
|
||||||
|
need_update = False
|
||||||
|
|
||||||
|
for field in self.REQUIRED_FIELDS:
|
||||||
|
value = self.aparams[field]
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
current_value = self.segment_info.get(field)
|
||||||
|
if isinstance(value, dict) and isinstance(current_value, dict):
|
||||||
|
if any(
|
||||||
|
current_value.get(key) != expected_value
|
||||||
|
for key, expected_value in value.items()
|
||||||
|
):
|
||||||
|
need_update = True
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
if value != current_value:
|
||||||
|
need_update = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if (
|
||||||
|
not need_update
|
||||||
|
and desired_enabled is not None
|
||||||
|
and desired_enabled != self.segment_info['enabled']
|
||||||
|
):
|
||||||
|
need_update = True
|
||||||
|
|
||||||
|
if need_update:
|
||||||
|
payload = {
|
||||||
|
'segment_id': self.segment_id,
|
||||||
|
'version_id': (
|
||||||
|
self.aparams['version_id']
|
||||||
|
or self.segment_info['version_id']
|
||||||
|
),
|
||||||
|
'access_group_id': (
|
||||||
|
self.aparams['access_group_id']
|
||||||
|
or self.segment_info['access_group_id']
|
||||||
|
),
|
||||||
|
'description': (
|
||||||
|
self.aparams['description']
|
||||||
|
or self.segment_info['description']
|
||||||
|
),
|
||||||
|
'dhcp_v4': (
|
||||||
|
self.aparams['dhcp_v4'] or self.segment_info.get('dhcp_v4')
|
||||||
|
),
|
||||||
|
'dhcp_v6': (
|
||||||
|
self.aparams['dhcp_v6'] or self.segment_info.get('dhcp_v6')
|
||||||
|
),
|
||||||
|
'display_name': (
|
||||||
|
self.aparams['display_name']
|
||||||
|
or self.segment_info['display_name']
|
||||||
|
),
|
||||||
|
'enabled': (
|
||||||
|
desired_enabled
|
||||||
|
if desired_enabled is not None
|
||||||
|
else self.segment_info['enabled']
|
||||||
|
),
|
||||||
|
'subnet_v4': (
|
||||||
|
self.aparams['subnet_v4']
|
||||||
|
or self.segment_info.get('subnet_v4')
|
||||||
|
),
|
||||||
|
'subnet_v6': (
|
||||||
|
self.aparams['subnet_v6']
|
||||||
|
or self.segment_info.get('subnet_v6')
|
||||||
|
),
|
||||||
|
'type': (
|
||||||
|
self.aparams['type'] or self.segment_info['type']
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.put,
|
||||||
|
arg_api_name='/restmachine/sdn/segment/update',
|
||||||
|
arg_json_body=payload,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.set_changed()
|
||||||
|
|
||||||
|
@DecortController.waypoint
|
||||||
|
@DecortController.checkmode
|
||||||
|
def segment_delete(
|
||||||
|
self,
|
||||||
|
force: bool = False,
|
||||||
|
):
|
||||||
|
version_id = self.aparams['version_id']
|
||||||
|
if version_id is None:
|
||||||
|
version_id = self.segment_info['version_id']
|
||||||
|
payload = {
|
||||||
|
'segment_id': self.segment_id,
|
||||||
|
'version_id': version_id,
|
||||||
|
}
|
||||||
|
self.decort_api_call(
|
||||||
|
arg_req_function=requests.delete,
|
||||||
|
arg_api_name='/restmachine/sdn/segment/delete',
|
||||||
|
arg_params=payload,
|
||||||
|
)
|
||||||
|
self.need_final_get = False
|
||||||
|
self.set_changed()
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_deleted(
|
||||||
|
obj='segment',
|
||||||
|
id=self.segment_id,
|
||||||
|
permanently=force,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.facts = {}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNSegment().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
142
library/decort_sdn_segment_list.py
Normal file
142
library/decort_sdn_segment_list.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_sdn_segment_list
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class DecortSDNSegmentList(DecortController):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=dict(
|
||||||
|
filter=dict(
|
||||||
|
type='dict',
|
||||||
|
apply_defaults=True,
|
||||||
|
options=dict(
|
||||||
|
enabled=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
is_synced=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
display_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
subnet=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
access_group_id=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
created_from=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
created_to=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
updated_from=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
updated_to=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
operation_status=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'Idle',
|
||||||
|
'SynchronizingAtCore',
|
||||||
|
'SynchronizingAtOVN',
|
||||||
|
'Synchronized',
|
||||||
|
'NoHypervisorAtOVN',
|
||||||
|
'FailedAtCore',
|
||||||
|
'TemporaryFailedAtCore',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pagination=dict(
|
||||||
|
type='dict',
|
||||||
|
apply_defaults=True,
|
||||||
|
options=dict(
|
||||||
|
number=dict(
|
||||||
|
type='int',
|
||||||
|
default=1,
|
||||||
|
),
|
||||||
|
size=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sorting=dict(
|
||||||
|
type='dict',
|
||||||
|
options=dict(
|
||||||
|
asc=dict(
|
||||||
|
type='bool',
|
||||||
|
default=True,
|
||||||
|
),
|
||||||
|
field=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'display_name',
|
||||||
|
'subnet',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
],
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.get_info()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
api_params: dict[str, Any] = dict()
|
||||||
|
|
||||||
|
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||||
|
for field, value in aparam_filter.items():
|
||||||
|
if value is not None:
|
||||||
|
api_params[field] = value
|
||||||
|
|
||||||
|
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||||
|
api_params['page'] = aparam_pagination['number']
|
||||||
|
api_params['per_page'] = aparam_pagination['size']
|
||||||
|
|
||||||
|
aparam_sorting: dict[str, Any] | None = self.aparams['sorting']
|
||||||
|
if aparam_sorting:
|
||||||
|
api_params['sort_by'] = aparam_sorting['field']
|
||||||
|
api_params['sort_order'] = (
|
||||||
|
'asc' if aparam_sorting['asc'] else 'desc'
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.decort_api_call(
|
||||||
|
arg_req_function=requests.get,
|
||||||
|
arg_api_name='/restmachine/sdn/segment/list',
|
||||||
|
arg_params=api_params,
|
||||||
|
accept_json_response=True,
|
||||||
|
)
|
||||||
|
self.facts = response.json()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortSDNSegmentList().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -136,7 +136,7 @@ class DecortSecurityGroup(DecortController):
|
|||||||
)
|
)
|
||||||
except sdk_exceptions.RequestException as e:
|
except sdk_exceptions.RequestException as e:
|
||||||
if (
|
if (
|
||||||
e.orig_exception.response
|
e.orig_exception.response is not None
|
||||||
and e.orig_exception.response.status_code == 404
|
and e.orig_exception.response.status_code == 404
|
||||||
):
|
):
|
||||||
self.message(
|
self.message(
|
||||||
@@ -301,10 +301,11 @@ class DecortSecurityGroup(DecortController):
|
|||||||
match aparam_rules['mode']:
|
match aparam_rules['mode']:
|
||||||
case self.SecurityGroupRuleMode.delete.value:
|
case self.SecurityGroupRuleMode.delete.value:
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
self.security_group_detele_rule(
|
self.sdk_checkmode(
|
||||||
security_group_id=self.id,
|
self.api.cloudapi.security_group.delete_rule)(
|
||||||
rule_id=rule['id'],
|
security_group_id=self.id,
|
||||||
)
|
rule_id=rule['id'],
|
||||||
|
)
|
||||||
case self.SecurityGroupRuleMode.match.value:
|
case self.SecurityGroupRuleMode.match.value:
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
if rule.get('id') is None:
|
if rule.get('id') is None:
|
||||||
@@ -318,10 +319,11 @@ class DecortSecurityGroup(DecortController):
|
|||||||
)
|
)
|
||||||
rules_ids_to_delete = sg_rules_ids - aparam_rules_ids
|
rules_ids_to_delete = sg_rules_ids - aparam_rules_ids
|
||||||
for rule_id in rules_ids_to_delete:
|
for rule_id in rules_ids_to_delete:
|
||||||
self.security_group_detele_rule(
|
self.sdk_checkmode(
|
||||||
security_group_id=self.id,
|
self.api.cloudapi.security_group.delete_rule)(
|
||||||
rule_id=rule_id,
|
security_group_id=self.id,
|
||||||
)
|
rule_id=rule_id,
|
||||||
|
)
|
||||||
case self.SecurityGroupRuleMode.update.value:
|
case self.SecurityGroupRuleMode.update.value:
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
if rule.get('id') is None:
|
if rule.get('id') is None:
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class DecortSecurityGroupList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class DecortStoragePolicy(DecortController):
|
|||||||
)
|
)
|
||||||
except sdk_exceptions.RequestException as e:
|
except sdk_exceptions.RequestException as e:
|
||||||
if (
|
if (
|
||||||
e.orig_exception.response
|
e.orig_exception.response is not None
|
||||||
and e.orig_exception.response.status_code == 404
|
and e.orig_exception.response.status_code == 404
|
||||||
):
|
):
|
||||||
self.message(
|
self.message(
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class DecortStoragePolicyList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class DecortTrunk(DecortController):
|
|||||||
)
|
)
|
||||||
except sdk_exceptions.RequestException as e:
|
except sdk_exceptions.RequestException as e:
|
||||||
if (
|
if (
|
||||||
e.orig_exception.response
|
e.orig_exception.response is not None
|
||||||
and e.orig_exception.response.status_code == 404
|
and e.orig_exception.response.status_code == 404
|
||||||
):
|
):
|
||||||
self.message(
|
self.message(
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class DecortTrunkList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ description: See L(Module Documentation,https://repository.basistech.ru/BASIS/de
|
|||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.decort_utils import DecortController
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
|
||||||
|
from dynamix_sdk import exceptions as sdk_exceptions
|
||||||
|
|
||||||
|
|
||||||
class DecortUser(DecortController):
|
class DecortUser(DecortController):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -43,16 +45,35 @@ class DecortUser(DecortController):
|
|||||||
self.facts = self.usermanager_whoami_result
|
self.facts = self.usermanager_whoami_result
|
||||||
self.id = self.facts['name']
|
self.id = self.facts['name']
|
||||||
|
|
||||||
user_get = self.user_get(id=self.id)
|
try:
|
||||||
for key in ['emailaddresses', 'data']:
|
user_model = self.api.cloudapi.user.get(user_name=self.id)
|
||||||
self.facts[key] = user_get[key]
|
except sdk_exceptions.RequestException as e:
|
||||||
|
if (
|
||||||
|
e.orig_exception.response is not None
|
||||||
|
and e.orig_exception.response.status_code == 404
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='user',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
self.facts.update(user_model.model_dump())
|
||||||
|
|
||||||
if self.aparams['resource_consumption']:
|
if self.aparams['resource_consumption']:
|
||||||
self.facts.update(self.user_resource_consumption())
|
self.facts.update(
|
||||||
|
self.api.ca.user.get_resource_consumption().model_dump()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delete duplicate self.facts['name']
|
||||||
|
del self.facts['user_name']
|
||||||
|
|
||||||
if self.aparams['api_methods']:
|
if self.aparams['api_methods']:
|
||||||
self.facts['api_methods'] = self.user_api_methods(id=self.id)
|
self.facts['api_methods'] = (
|
||||||
|
self.api.cloudapi.user.api_list(user_name=self.id).model_dump()
|
||||||
|
)
|
||||||
search_string = self.aparams['objects_search']
|
search_string = self.aparams['objects_search']
|
||||||
if search_string:
|
if search_string:
|
||||||
self.facts['objects_search'] = self.user_objects_search(
|
self.facts['objects_search'] = self.user_objects_search(
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ class decort_vins(DecortController):
|
|||||||
|
|
||||||
self.vins_id = 0
|
self.vins_id = 0
|
||||||
self.vins_level = "" # "ID" if specified by ID, "RG" - at resource group, "ACC" - at account level
|
self.vins_level = "" # "ID" if specified by ID, "RG" - at resource group, "ACC" - at account level
|
||||||
vins_facts = None # will hold ViNS facts
|
|
||||||
validated_rg_id = 0
|
validated_rg_id = 0
|
||||||
rg_facts = None # will hold RG facts
|
rg_facts = None # will hold RG facts
|
||||||
validated_acc_id = 0
|
validated_acc_id = 0
|
||||||
@@ -28,15 +27,28 @@ class decort_vins(DecortController):
|
|||||||
if arg_amodule.params['vins_id']:
|
if arg_amodule.params['vins_id']:
|
||||||
# expect existing ViNS with the specified ID
|
# expect existing ViNS with the specified ID
|
||||||
# This call to vins_find will abort the module if no ViNS with such ID is present
|
# This call to vins_find will abort the module if no ViNS with such ID is present
|
||||||
self.vins_id, self.vins_facts = self.vins_find(arg_amodule.params['vins_id'],check_state=False)
|
self.vins_id, self._vins_info = self.vins_find(
|
||||||
|
arg_amodule.params['vins_id'],
|
||||||
|
check_state=False,
|
||||||
|
)
|
||||||
if self.vins_id == 0:
|
if self.vins_id == 0:
|
||||||
|
if arg_amodule.params['state'] == 'absent':
|
||||||
|
self.exit()
|
||||||
|
else:
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='VINS',
|
||||||
|
id=arg_amodule.params['vins_id'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
if self._vins_info is None:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['msg'] = "Specified ViNS ID {} not found.".format(arg_amodule.params['vins_id'])
|
self.result['msg'] = "Specified ViNS ID {} not found.".format(arg_amodule.params['vins_id'])
|
||||||
self.amodule.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
self.vins_level = "ID"
|
self.vins_level = "ID"
|
||||||
#raise Exception(self.vins_facts)
|
validated_acc_id = self._vins_info.account_id
|
||||||
validated_acc_id = self.vins_facts['accountId']
|
validated_rg_id = self._vins_info.rg_id
|
||||||
validated_rg_id = self.vins_facts['rgId']
|
|
||||||
|
|
||||||
elif arg_amodule.params['rg_id']:
|
elif arg_amodule.params['rg_id']:
|
||||||
# expect ViNS @ RG level in the RG with specified ID
|
# expect ViNS @ RG level in the RG with specified ID
|
||||||
@@ -44,14 +56,16 @@ class decort_vins(DecortController):
|
|||||||
# This call to rg_find will abort the module if no RG with such ID is present
|
# This call to rg_find will abort the module if no RG with such ID is present
|
||||||
validated_rg_id, rg_facts = self.rg_find(0, # account ID set to 0 as we search for RG by RG ID
|
validated_rg_id, rg_facts = self.rg_find(0, # account ID set to 0 as we search for RG by RG ID
|
||||||
arg_amodule.params['rg_id'], arg_rg_name="")
|
arg_amodule.params['rg_id'], arg_rg_name="")
|
||||||
validated_acc_id = rg_facts['accountId']
|
validated_acc_id = rg_facts.account_id
|
||||||
|
|
||||||
# This call to vins_find may return vins_id=0 if no ViNS found
|
# This call to vins_find may return vins_id=0 if no ViNS found
|
||||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
self.vins_id, self._vins_info = self.vins_find(
|
||||||
account_id=0,
|
vins_id=0,
|
||||||
rg_id=arg_amodule.params['rg_id'],
|
vins_name=arg_amodule.params['vins_name'],
|
||||||
rg_facts=rg_facts,
|
account_id=0,
|
||||||
check_state=False)
|
rg_id=arg_amodule.params['rg_id'],
|
||||||
|
check_state=False,
|
||||||
|
)
|
||||||
# TODO: add checks and setup ViNS presence flags accordingly
|
# TODO: add checks and setup ViNS presence flags accordingly
|
||||||
pass
|
pass
|
||||||
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
|
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
|
||||||
@@ -66,27 +80,39 @@ class decort_vins(DecortController):
|
|||||||
# expect ViNS @ RG level in the RG with specified name under specified account
|
# expect ViNS @ RG level in the RG with specified name under specified account
|
||||||
# RG with the specified name must be present under the account, otherwise abort the module
|
# RG with the specified name must be present under the account, otherwise abort the module
|
||||||
validated_rg_id, rg_facts = self.rg_find(validated_acc_id, 0, arg_amodule.params['rg_name'])
|
validated_rg_id, rg_facts = self.rg_find(validated_acc_id, 0, arg_amodule.params['rg_name'])
|
||||||
if (not validated_rg_id or
|
if (not validated_rg_id or rg_facts is None or
|
||||||
rg_facts['status'] in ["DESTROYING", "DESTROYED", "DELETING", "DELETED", "DISABLING", "ENABLING"]):
|
rg_facts.status in [
|
||||||
|
sdk_types.ResourceGroupStatus.DESTROYING,
|
||||||
|
sdk_types.ResourceGroupStatus.DESTROYED,
|
||||||
|
sdk_types.ResourceGroupStatus.DELETED,
|
||||||
|
sdk_types.ResourceGroupStatus.DISABLING,
|
||||||
|
sdk_types.ResourceGroupStatus.ENABLING,
|
||||||
|
]
|
||||||
|
):
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['msg'] = "RG name '{}' not found or has invalid state.".format(arg_amodule.params['rg_name'])
|
self.result['msg'] = "RG name '{}' not found or has invalid state.".format(arg_amodule.params['rg_name'])
|
||||||
self.amodule.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
# This call to vins_find may return vins_id=0 if no ViNS with this name found under specified RG
|
# This call to vins_find may return vins_id=0 if no ViNS with this name found under specified RG
|
||||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
# (account_id) set to 0, as we are looking for ViNS under RG
|
||||||
account_id=0, # set to 0, as we are looking for ViNS under RG
|
self.vins_id, self._vins_info = self.vins_find(
|
||||||
rg_id=validated_rg_id,
|
vins_id=0,
|
||||||
rg_facts=rg_facts,
|
vins_name=arg_amodule.params['vins_name'],
|
||||||
check_state=False)
|
account_id=0,
|
||||||
|
rg_id=validated_rg_id,
|
||||||
|
check_state=False,
|
||||||
|
)
|
||||||
self.vins_level = "RG"
|
self.vins_level = "RG"
|
||||||
# TODO: add checks and setup ViNS presence flags accordingly
|
# TODO: add checks and setup ViNS presence flags accordingly
|
||||||
else: # At this point we know for sure that rg_name="" and rg_id=0
|
else: # At this point we know for sure that rg_name="" and rg_id=0
|
||||||
# So we expect ViNS @ account level
|
# So we expect ViNS @ account level
|
||||||
# This call to vins_find may return vins_id=0 if no ViNS found
|
# This call to vins_find may return vins_id=0 if no ViNS found
|
||||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
self.vins_id, self._vins_info = self.vins_find(
|
||||||
account_id=validated_acc_id,
|
vins_id=0,
|
||||||
rg_id=0,
|
vins_name=arg_amodule.params['vins_name'],
|
||||||
rg_facts=rg_facts,
|
account_id=validated_acc_id,
|
||||||
check_state=False)
|
rg_id=0,
|
||||||
|
check_state=False,
|
||||||
|
)
|
||||||
self.vins_level = "ACC"
|
self.vins_level = "ACC"
|
||||||
# TODO: add checks and setup ViNS presence flags accordingly
|
# TODO: add checks and setup ViNS presence flags accordingly
|
||||||
else:
|
else:
|
||||||
@@ -106,73 +132,142 @@ class decort_vins(DecortController):
|
|||||||
self.rg_id = validated_rg_id
|
self.rg_id = validated_rg_id
|
||||||
self.acc_id = validated_acc_id
|
self.acc_id = validated_acc_id
|
||||||
|
|
||||||
if self.vins_id and self.vins_facts['status'] != 'DESTROYED':
|
if (
|
||||||
|
self._vins_info
|
||||||
|
and self._vins_info.status != sdk_types.VINSStatus.DESTROYED
|
||||||
|
):
|
||||||
self.check_amodule_args_for_change()
|
self.check_amodule_args_for_change()
|
||||||
else:
|
else:
|
||||||
self.check_amodule_args_for_create()
|
self.check_amodule_args_for_create()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
self.vins_id = self.vins_provision(self.amodule.params['vins_name'],
|
security_group_mode = self.amodule.params['security_group_mode']
|
||||||
self.acc_id, self.rg_id,
|
if security_group_mode is None:
|
||||||
self.amodule.params['ipcidr'],
|
security_group_mode = False
|
||||||
self.amodule.params['ext_net_id'], self.amodule.params['ext_ip_addr'],
|
self.message(
|
||||||
self.amodule.params['description'],
|
msg=self.MESSAGES.default_value_used(
|
||||||
zone_id=self.amodule.params['zone_id'],
|
param_name='security_group_mode',
|
||||||
|
default_value=security_group_mode
|
||||||
|
),
|
||||||
|
warning=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
enable_default_gateway = self.amodule.params['enable_default_gateway']
|
||||||
|
if enable_default_gateway is None:
|
||||||
|
enable_default_gateway = True
|
||||||
|
self.message(
|
||||||
|
msg=self.MESSAGES.default_value_used(
|
||||||
|
param_name='enable_default_gateway',
|
||||||
|
default_value=enable_default_gateway
|
||||||
|
),
|
||||||
|
warning=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.vins_id = self.vins_provision(
|
||||||
|
vins_name=self.amodule.params['vins_name'],
|
||||||
|
account_id=self.acc_id,
|
||||||
|
rg_id=self.rg_id,
|
||||||
|
ipcidr=self.amodule.params['ipcidr'],
|
||||||
|
ext_net_id=self.amodule.params['ext_net_id'],
|
||||||
|
ext_ip_addr=self.amodule.params['ext_ip_addr'],
|
||||||
|
desc=self.amodule.params['description'],
|
||||||
|
zone_id=self.amodule.params['zone_id'],
|
||||||
|
security_group_mode=security_group_mode,
|
||||||
|
enable_default_gateway=enable_default_gateway,
|
||||||
)
|
)
|
||||||
|
if self.vins_id:
|
||||||
if self.amodule.params['mgmtaddr'] or self.amodule.params['connect_to']:
|
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
|
||||||
if self.amodule.params['connect_to']:
|
if self.amodule.params['connect_to']:
|
||||||
self.vins_update_ifaces(self.vins_facts,self.amodule.params['connect_to'],)
|
self.vins_update_ifaces(
|
||||||
|
self.vins_info,
|
||||||
|
self.amodule.params['connect_to'],
|
||||||
|
)
|
||||||
if self.amodule.params['mgmtaddr']:
|
if self.amodule.params['mgmtaddr']:
|
||||||
self.vins_update_mgmt(self.vins_facts,self.amodule.params['mgmtaddr'])
|
self.vins_update_mgmt(
|
||||||
|
self.vins_info,
|
||||||
|
self.amodule.params['mgmtaddr'],
|
||||||
|
)
|
||||||
return
|
return
|
||||||
def action(self,d_state='',restore=False):
|
|
||||||
if restore == True:
|
def action(self, d_state='', restore=False):
|
||||||
self.vins_restore(arg_vins_id=self.vins_id)
|
if restore:
|
||||||
self.vins_state(self.vins_facts, 'enabled')
|
self.sdk_checkmode(self.api.cloudapi.vins.restore)(
|
||||||
self.vins_facts['status'] = "ENABLED"
|
vins_id=self.vins_info.id,
|
||||||
self.vins_facts['VNFDev']['techStatus'] = "STARTED"
|
)
|
||||||
|
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||||
self.vins_update_extnet(self.vins_facts,
|
self.vins_state(self.vins_info, 'enabled')
|
||||||
self.amodule.params['ext_net_id'],
|
|
||||||
self.amodule.params['ext_ip_addr'],
|
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||||
)
|
|
||||||
|
if (
|
||||||
if d_state == 'enabled' and self.vins_facts['status'] == "DISABLED":
|
self.amodule.params['ext_net_id'] is not None
|
||||||
self.vins_state(self.vins_facts, d_state)
|
or self.amodule.params['ext_ip_addr'] is not None
|
||||||
self.vins_facts['status'] = "ENABLED"
|
):
|
||||||
self.vins_facts['VNFDev']['techStatus'] = "STARTED"
|
self.vins_update_extnet(
|
||||||
|
self.vins_info,
|
||||||
|
self.amodule.params['ext_net_id'],
|
||||||
|
self.amodule.params['ext_ip_addr'],
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
d_state == 'enabled'
|
||||||
|
and self.vins_info.status == sdk_types.VINSStatus.DISABLED
|
||||||
|
):
|
||||||
|
self.vins_state(self.vins_info, d_state)
|
||||||
|
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||||
d_state = ''
|
d_state = ''
|
||||||
|
|
||||||
if self.vins_facts['status'] == "ENABLED" and self.vins_facts['VNFDev']['techStatus'] == "STARTED":
|
if (
|
||||||
self.vins_update_ifaces(self.vins_facts,
|
self.vins_info.status == sdk_types.VINSStatus.ENABLED
|
||||||
self.amodule.params['connect_to'],
|
and self.vins_info.vnfdev.tech_status == (
|
||||||
)
|
sdk_types.VNFDevTechStatus.STARTED
|
||||||
|
)
|
||||||
|
):
|
||||||
|
self.vins_update_ifaces(
|
||||||
|
self.vins_info,
|
||||||
|
self.amodule.params['connect_to'],
|
||||||
|
)
|
||||||
if self.result['changed']:
|
if self.result['changed']:
|
||||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||||
self.vins_update_mgmt(self.vins_facts,
|
|
||||||
self.amodule.params['mgmtaddr'],
|
self.vins_update_mgmt(
|
||||||
)
|
self.vins_info,
|
||||||
|
self.amodule.params['mgmtaddr'],
|
||||||
|
)
|
||||||
|
|
||||||
if d_state != '':
|
if d_state != '':
|
||||||
self.vins_state(self.vins_facts, d_state)
|
self.vins_state(self.vins_info, d_state)
|
||||||
|
|
||||||
aparam_zone_id = self.aparams['zone_id']
|
aparam_zone_id = self.aparams['zone_id']
|
||||||
if aparam_zone_id is not None and aparam_zone_id != self.vins_facts['zoneId']:
|
if (
|
||||||
self.vins_migrate_to_zone(
|
aparam_zone_id is not None
|
||||||
net_id=self.vins_id,
|
and aparam_zone_id != self.vins_info.zone_id
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.vins.migrate_to_zone)(
|
||||||
|
vins_id=self.vins_info.id,
|
||||||
zone_id=aparam_zone_id,
|
zone_id=aparam_zone_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.amodule.params['enable_default_gateway']
|
||||||
|
!= self.vins_info.enable_default_gateway
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.cloudbroker.vins.update)(
|
||||||
|
vins_id=self.vins_info.id,
|
||||||
|
enable_default_gateway=self.amodule.params['enable_default_gateway'], # noqa: E501
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
self.vins_delete(self.vins_id, self.amodule.params['permanently'])
|
self.sdk_checkmode(self.api.cloudapi.vins.delete)(
|
||||||
self.vins_facts['status'] = 'DESTROYED'
|
vins_id=self.vins_info.id,
|
||||||
|
permanently=self.amodule.params['permanently'],
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
def nop(self):
|
def nop(self):
|
||||||
"""No operation (NOP) handler for ViNS management by decort_vins module.
|
"""No operation (NOP) handler for ViNS management by decort_vins module.
|
||||||
This function is intended to be called from the main switch construct of the module
|
This function is intended to be called from the main switch construct of the module
|
||||||
@@ -181,23 +276,27 @@ class decort_vins(DecortController):
|
|||||||
"""
|
"""
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
if self.vins_id:
|
if self._vins_info:
|
||||||
self.result['msg'] = ("No state change required for ViNS ID {} because of its "
|
self.result['msg'] = (
|
||||||
"current status '{}'.").format(self.vins_id, self.vins_facts['status'])
|
f'No state change required for ViNS ID {self._vins_info.id} '
|
||||||
|
f'because of its "current status "{self._vins_info.status}".'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
self.result['msg'] = ("No state change to '{}' can be done for "
|
||||||
"non-existent ViNS instance.").format(self.amodule.params['state'])
|
"non-existent ViNS instance.").format(self.amodule.params['state'])
|
||||||
return
|
return
|
||||||
|
|
||||||
def error(self):
|
def error(self):
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
if self.vins_id:
|
if self._vins_info:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = ("Invalid target state '{}' requested for ViNS ID {} in the "
|
self.result['msg'] = (
|
||||||
"current status '{}'").format(self.vins_id,
|
f'Invalid target state "{self.amodule.params['state']}" '
|
||||||
self.amodule.params['state'],
|
f'requested for ViNS ID {self._vins_info.id} in the '
|
||||||
self.vins_facts['status'])
|
f'current status "{self._vins_info.status}"'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
@@ -205,6 +304,7 @@ class decort_vins(DecortController):
|
|||||||
"ViNS name '{}'").format(self.amodule.params['state'],
|
"ViNS name '{}'").format(self.amodule.params['state'],
|
||||||
self.amodule.params['vins_name'])
|
self.amodule.params['vins_name'])
|
||||||
return
|
return
|
||||||
|
|
||||||
def package_facts(self, arg_check_mode=False):
|
def package_facts(self, arg_check_mode=False):
|
||||||
"""Package a dictionary of ViNS facts according to the decort_vins module specification.
|
"""Package a dictionary of ViNS facts according to the decort_vins module specification.
|
||||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||||
@@ -222,40 +322,12 @@ class decort_vins(DecortController):
|
|||||||
# in check mode return immediately with the default values
|
# in check mode return immediately with the default values
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
if self.vins_facts is None:
|
if self._vins_info is None:
|
||||||
# if void facts provided - change state value to ABSENT and return
|
# if void facts provided - change state value to ABSENT and return
|
||||||
ret_dict['state'] = "ABSENT"
|
ret_dict['state'] = "ABSENT"
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
ret_dict['id'] = self.vins_facts['id']
|
return self._vins_info.model_dump()
|
||||||
ret_dict['name'] = self.vins_facts['name']
|
|
||||||
ret_dict['state'] = self.vins_facts['status']
|
|
||||||
ret_dict['account_id'] = self.vins_facts['accountId']
|
|
||||||
ret_dict['rg_id'] = self.vins_facts['rgId']
|
|
||||||
ret_dict['int_net_addr'] = self.vins_facts['network']
|
|
||||||
ret_dict['gid'] = self.vins_facts['gid']
|
|
||||||
custom_interfaces = list(filter(lambda i: i['type']=="CUSTOM",self.vins_facts['VNFDev']['interfaces']))
|
|
||||||
if custom_interfaces:
|
|
||||||
ret_dict['custom_net_addr'] = []
|
|
||||||
for runner in custom_interfaces:
|
|
||||||
ret_dict['custom_net_addr'].append(runner['ipAddress'])
|
|
||||||
mgmt_interfaces = list(filter(lambda i: i['listenSsh'] and i['name']!="ens9",self.vins_facts['VNFDev']['interfaces']))
|
|
||||||
if mgmt_interfaces:
|
|
||||||
ret_dict['ssh_ipaddr'] = []
|
|
||||||
for runner in mgmt_interfaces:
|
|
||||||
ret_dict['ssh_ipaddr'].append(runner['ipAddress'])
|
|
||||||
ret_dict['ssh_password'] = self.vins_facts['VNFDev']['config']['mgmt']['password']
|
|
||||||
ret_dict['ssh_port'] = 9022
|
|
||||||
if self.vins_facts['vnfs'].get('GW'):
|
|
||||||
gw_config = self.vins_facts['vnfs']['GW']['config']
|
|
||||||
ret_dict['ext_ip_addr'] = gw_config['ext_net_ip']
|
|
||||||
ret_dict['ext_net_id'] = gw_config['ext_net_id']
|
|
||||||
else:
|
|
||||||
ret_dict['ext_ip_addr'] = ""
|
|
||||||
ret_dict['ext_net_id'] = -1
|
|
||||||
ret_dict['zone_id'] = self.vins_facts['zoneId']
|
|
||||||
|
|
||||||
return ret_dict
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -276,11 +348,9 @@ class decort_vins(DecortController):
|
|||||||
),
|
),
|
||||||
ext_net_id=dict(
|
ext_net_id=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=-1,
|
|
||||||
),
|
),
|
||||||
ext_ip_addr=dict(
|
ext_ip_addr=dict(
|
||||||
type='str',
|
type='str',
|
||||||
default='',
|
|
||||||
),
|
),
|
||||||
ipcidr=dict(
|
ipcidr=dict(
|
||||||
type='str',
|
type='str',
|
||||||
@@ -304,7 +374,6 @@ class decort_vins(DecortController):
|
|||||||
),
|
),
|
||||||
state=dict(
|
state=dict(
|
||||||
type='str',
|
type='str',
|
||||||
default='present',
|
|
||||||
choices=[
|
choices=[
|
||||||
'absent',
|
'absent',
|
||||||
'disabled',
|
'disabled',
|
||||||
@@ -335,6 +404,12 @@ class decort_vins(DecortController):
|
|||||||
zone_id=dict(
|
zone_id=dict(
|
||||||
type=int,
|
type=int,
|
||||||
),
|
),
|
||||||
|
enable_default_gateway=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
|
security_group_mode=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
required_one_of=[
|
required_one_of=[
|
||||||
@@ -347,6 +422,34 @@ class decort_vins(DecortController):
|
|||||||
if self.check_aparam_zone_id() is False:
|
if self.check_aparam_zone_id() is False:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.amodule.params['ext_ip_addr']
|
||||||
|
and self.amodule.params['ext_net_id'] is None
|
||||||
|
and self.vins_info.vnfs.gw is None
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
msg=(
|
||||||
|
'Check for parameter "ext_net_id" failed: '
|
||||||
|
'the "ext_net_id" parameter must be specified '
|
||||||
|
'if the "ext_ip_addr" parameter is passed and '
|
||||||
|
'VINS is not connected to an external network.'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
check_errors = True
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['security_group_mode'] is not None
|
||||||
|
and self.vins_info.security_group_mode != self.aparams['security_group_mode']
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
msg=(
|
||||||
|
'Check for parameter "security_group_mode" failed: '
|
||||||
|
'"security_group_mode" cannot be changed '
|
||||||
|
'for existing ViNS'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
check_errors = True
|
||||||
|
|
||||||
if check_errors:
|
if check_errors:
|
||||||
self.exit(fail=True)
|
self.exit(fail=True)
|
||||||
|
|
||||||
@@ -384,36 +487,54 @@ class decort_vins(DecortController):
|
|||||||
# if cconfig_save is true, only config save without other updates
|
# if cconfig_save is true, only config save without other updates
|
||||||
vins_should_exist = False
|
vins_should_exist = False
|
||||||
|
|
||||||
if self.vins_id:
|
if self._vins_info:
|
||||||
vins_should_exist = True
|
vins_should_exist = True
|
||||||
if self.vins_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING"]:
|
if self._vins_info.status in [
|
||||||
|
sdk_types.VINSStatus.MODELED,
|
||||||
|
sdk_types.VINSStatus.DISABLING,
|
||||||
|
sdk_types.VINSStatus.ENABLING,
|
||||||
|
sdk_types.VINSStatus.DELETING,
|
||||||
|
sdk_types.VINSStatus.DESTROYING,
|
||||||
|
]:
|
||||||
# error: nothing can be done to existing ViNS in the listed statii regardless of
|
# error: nothing can be done to existing ViNS in the listed statii regardless of
|
||||||
# the requested state
|
# the requested state
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = ("No change can be done for existing ViNS ID {} because of its current "
|
self.result['msg'] = (
|
||||||
"status '{}'").format(self.vins_id, self.vins_facts['status'])
|
f'No change can be done for existing '
|
||||||
elif self.vins_facts['status'] == "DISABLED":
|
f'ViNS ID {self.vins_id} because of its '
|
||||||
|
f'current status {self._vins_info.status}'
|
||||||
|
)
|
||||||
|
elif self._vins_info.status == sdk_types.VINSStatus.DISABLED:
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
self.delete()
|
self.delete()
|
||||||
vins_should_exist = False
|
vins_should_exist = False
|
||||||
elif amodule.params['state'] in ('present', 'disabled'):
|
elif (
|
||||||
|
amodule.params['state'] is None
|
||||||
|
or amodule.params['state'] in ('present', 'disabled')
|
||||||
|
):
|
||||||
# update ViNS, leave in disabled state
|
# update ViNS, leave in disabled state
|
||||||
self.action()
|
self.action()
|
||||||
elif amodule.params['state'] == 'enabled':
|
elif amodule.params['state'] == 'enabled':
|
||||||
# update ViNS and enable
|
# update ViNS and enable
|
||||||
self.action('enabled')
|
self.action('enabled')
|
||||||
elif self.vins_facts['status'] in ["CREATED", "ENABLED"]:
|
elif self._vins_info.status in [
|
||||||
|
sdk_types.VINSStatus.CREATED,
|
||||||
|
sdk_types.VINSStatus.ENABLED,
|
||||||
|
]:
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
self.delete()
|
self.delete()
|
||||||
vins_should_exist = False
|
vins_should_exist = False
|
||||||
elif amodule.params['state'] in ('present', 'enabled'):
|
elif (
|
||||||
|
amodule.params['state'] is None
|
||||||
|
or amodule.params['state'] in ('present', 'enabled')
|
||||||
|
):
|
||||||
# update ViNS
|
# update ViNS
|
||||||
self.action()
|
self.action()
|
||||||
elif amodule.params['state'] == 'disabled':
|
elif amodule.params['state'] == 'disabled':
|
||||||
# disable and update ViNS
|
# disable and update ViNS
|
||||||
self.action('disabled')
|
self.action('disabled')
|
||||||
elif self.vins_facts['status'] == "DELETED":
|
elif self._vins_info.status == sdk_types.VINSStatus.DELETED:
|
||||||
if amodule.params['state'] in ['present', 'enabled']:
|
if amodule.params['state'] in ['present', 'enabled']:
|
||||||
# restore and enable
|
# restore and enable
|
||||||
self.action(restore=True)
|
self.action(restore=True)
|
||||||
@@ -426,28 +547,48 @@ class decort_vins(DecortController):
|
|||||||
elif amodule.params['state'] == 'disabled':
|
elif amodule.params['state'] == 'disabled':
|
||||||
self.error()
|
self.error()
|
||||||
vins_should_exist = False
|
vins_should_exist = False
|
||||||
elif self.vins_facts['status'] == "DESTROYED":
|
elif self._vins_info.status == sdk_types.VINSStatus.DESTROYED:
|
||||||
if amodule.params['state'] in ('present', 'enabled'):
|
state = amodule.params['state']
|
||||||
|
if state is None:
|
||||||
|
state = 'present'
|
||||||
|
self.message(
|
||||||
|
msg=(
|
||||||
|
f'State not specified, '
|
||||||
|
f'default value "{state}" will be used.'
|
||||||
|
),
|
||||||
|
warning=True,
|
||||||
|
)
|
||||||
|
if state in ('present', 'enabled'):
|
||||||
# need to re-provision ViNS;
|
# need to re-provision ViNS;
|
||||||
self.create()
|
self.create()
|
||||||
vins_should_exist = True
|
vins_should_exist = True
|
||||||
elif amodule.params['state'] == 'absent':
|
elif state == 'absent':
|
||||||
self.nop()
|
self.nop()
|
||||||
vins_should_exist = False
|
vins_should_exist = False
|
||||||
elif amodule.params['state'] == 'disabled':
|
elif state == 'disabled':
|
||||||
self.error()
|
self.error()
|
||||||
else:
|
else:
|
||||||
|
state = amodule.params['state']
|
||||||
|
if state is None:
|
||||||
|
state = 'present'
|
||||||
|
self.message(
|
||||||
|
msg=(
|
||||||
|
f'State not specified, '
|
||||||
|
f'default value "{state}" will be used.'
|
||||||
|
),
|
||||||
|
warning=True,
|
||||||
|
)
|
||||||
# Preexisting ViNS was not found.
|
# Preexisting ViNS was not found.
|
||||||
vins_should_exist = False # we will change it back to True if ViNS is created or restored
|
vins_should_exist = False # we will change it back to True if ViNS is created or restored
|
||||||
# If requested state is 'absent' - nothing to do
|
# If requested state is 'absent' - nothing to do
|
||||||
if amodule.params['state'] == 'absent':
|
if state == 'absent':
|
||||||
self.nop()
|
self.nop()
|
||||||
elif amodule.params['state'] in ('present', 'enabled'):
|
elif state in ('present', 'enabled'):
|
||||||
self.check_amodule_argument('vins_name')
|
self.check_amodule_argument('vins_name')
|
||||||
# as we already have account ID and RG ID we can create ViNS and get vins_id on success
|
# as we already have account ID and RG ID we can create ViNS and get vins_id on success
|
||||||
self.create()
|
self.create()
|
||||||
vins_should_exist = True
|
vins_should_exist = True
|
||||||
elif amodule.params['state'] == 'disabled':
|
elif state == 'disabled':
|
||||||
self.error()
|
self.error()
|
||||||
#
|
#
|
||||||
# conditional switch end - complete module run
|
# conditional switch end - complete module run
|
||||||
@@ -457,7 +598,7 @@ class decort_vins(DecortController):
|
|||||||
else:
|
else:
|
||||||
# prepare ViNS facts to be returned as part of self.result and then call exit_json(...)
|
# prepare ViNS facts to be returned as part of self.result and then call exit_json(...)
|
||||||
if self.result['changed']:
|
if self.result['changed']:
|
||||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||||
self.result['facts'] = self.package_facts(amodule.check_mode)
|
self.result['facts'] = self.package_facts(amodule.check_mode)
|
||||||
amodule.exit_json(**self.result)
|
amodule.exit_json(**self.result)
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class DecortVINSList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class decort_vm(DecortController):
|
|||||||
|
|
||||||
validated_acc_id = 0
|
validated_acc_id = 0
|
||||||
validated_rg_id = 0
|
validated_rg_id = 0
|
||||||
validated_rg_facts = None
|
validated_rg_model = None
|
||||||
|
|
||||||
self.vm_to_clone_id = 0
|
self.vm_to_clone_id = 0
|
||||||
self.vm_to_clone_info = None
|
self.vm_to_clone_info = None
|
||||||
@@ -118,21 +118,21 @@ class decort_vm(DecortController):
|
|||||||
self.fail_json(**self.result)
|
self.fail_json(**self.result)
|
||||||
# fail the module -> exit
|
# fail the module -> exit
|
||||||
# now validate RG
|
# now validate RG
|
||||||
validated_rg_id, validated_rg_facts = self.rg_find(validated_acc_id,
|
validated_rg_id, validated_rg_model = self.rg_find(validated_acc_id,
|
||||||
arg_amodule.params['rg_id'],
|
arg_amodule.params['rg_id'],
|
||||||
arg_amodule.params['rg_name'])
|
arg_amodule.params['rg_name'])
|
||||||
if not validated_rg_id:
|
if not validated_rg_id or not validated_rg_model:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['changed'] = False
|
self.result['changed'] = False
|
||||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||||
arg_amodule.params['rg_name'])
|
arg_amodule.params['rg_name'])
|
||||||
self.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
# fail the module - exit
|
# fail the module - exit
|
||||||
|
|
||||||
self.rg_id = validated_rg_id
|
self.rg_id = validated_rg_id
|
||||||
arg_amodule.params['rg_id'] = validated_rg_id
|
arg_amodule.params['rg_id'] = validated_rg_id
|
||||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
arg_amodule.params['rg_name'] = validated_rg_model.name
|
||||||
self.acc_id = validated_rg_facts['accountId']
|
self.acc_id = validated_rg_model.account_id
|
||||||
|
|
||||||
# at this point we are ready to locate Compute, and if anything fails now, then it must be
|
# at this point we are ready to locate Compute, and if anything fails now, then it must be
|
||||||
# because this Compute does not exist or something goes wrong in the upstream API
|
# because this Compute does not exist or something goes wrong in the upstream API
|
||||||
@@ -456,9 +456,9 @@ class decort_vm(DecortController):
|
|||||||
image_id=self.amodule.params['image_id'],
|
image_id=self.amodule.params['image_id'],
|
||||||
account_id=self.acc_id)
|
account_id=self.acc_id)
|
||||||
|
|
||||||
if validated_bdisk_size <= image_facts['size']:
|
if validated_bdisk_size <= image_facts['size_gb']:
|
||||||
# adjust disk size to the minimum allowed by OS image, which will be used to spin off this Compute
|
# adjust disk size to the minimum allowed by OS image, which will be used to spin off this Compute
|
||||||
validated_bdisk_size = image_facts['size']
|
validated_bdisk_size = image_facts['size_gb']
|
||||||
|
|
||||||
# NOTE: due to a libvirt "feature", that impacts management of a VM created without any network interfaces,
|
# NOTE: due to a libvirt "feature", that impacts management of a VM created without any network interfaces,
|
||||||
# we create KVM VM in HALTED state.
|
# we create KVM VM in HALTED state.
|
||||||
@@ -495,6 +495,10 @@ class decort_vm(DecortController):
|
|||||||
if numa_affinity is None:
|
if numa_affinity is None:
|
||||||
numa_affinity = 'none'
|
numa_affinity = 'none'
|
||||||
|
|
||||||
|
clock_sync_mode = self.aparams['clock_sync_mode']
|
||||||
|
if clock_sync_mode is None:
|
||||||
|
clock_sync_mode = 'default'
|
||||||
|
|
||||||
chipset = self.amodule.params['chipset']
|
chipset = self.amodule.params['chipset']
|
||||||
if chipset is None:
|
if chipset is None:
|
||||||
chipset = 'Q35'
|
chipset = 'Q35'
|
||||||
@@ -552,6 +556,8 @@ class decort_vm(DecortController):
|
|||||||
zone_id=self.aparams['zone_id'],
|
zone_id=self.aparams['zone_id'],
|
||||||
storage_policy_id=self.aparams['storage_policy_id'],
|
storage_policy_id=self.aparams['storage_policy_id'],
|
||||||
os_version=self.aparams['os_version'],
|
os_version=self.aparams['os_version'],
|
||||||
|
cpu_alignment_profile_name=self.aparams['cpu_alignment_profile_name'],
|
||||||
|
clock_sync_mode=clock_sync_mode,
|
||||||
)
|
)
|
||||||
self.comp_should_exist = True
|
self.comp_should_exist = True
|
||||||
|
|
||||||
@@ -614,6 +620,12 @@ class decort_vm(DecortController):
|
|||||||
custom_fields=custom_fields_fields,
|
custom_fields=custom_fields_fields,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.aparams['folder_id'] is not None:
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.vmfolder.computes_move)(
|
||||||
|
compute_ids=[self.comp_info['id']],
|
||||||
|
folder_id=self.aparams['folder_id'],
|
||||||
|
)
|
||||||
|
|
||||||
# read in Compute facts once more after all initial setup is complete
|
# read in Compute facts once more after all initial setup is complete
|
||||||
_, self.comp_info, _ = self.compute_find(
|
_, self.comp_info, _ = self.compute_find(
|
||||||
comp_id=self.comp_id,
|
comp_id=self.comp_id,
|
||||||
@@ -635,7 +647,11 @@ class decort_vm(DecortController):
|
|||||||
"""Compute destroy handler for VM management by decort_vm module.
|
"""Compute destroy handler for VM management by decort_vm module.
|
||||||
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.sdk_checkmode(self.api.ca.compute.delete)(
|
||||||
|
vm_id=self.comp_id,
|
||||||
|
detach_disks=True,
|
||||||
|
permanently=True,
|
||||||
|
)
|
||||||
self.comp_id, self.comp_info, _ = self._compute_get_by_id(self.comp_id)
|
self.comp_id, self.comp_info, _ = self._compute_get_by_id(self.comp_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -669,6 +685,16 @@ class decort_vm(DecortController):
|
|||||||
**self.compute_update_args,
|
**self.compute_update_args,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
folder_id = self.aparams['folder_id']
|
||||||
|
if (
|
||||||
|
folder_id is not None
|
||||||
|
and folder_id != (self.comp_info.get('folder_id') or 0)
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.vmfolder.computes_move)(
|
||||||
|
compute_ids=[self.comp_info['id']],
|
||||||
|
folder_id=folder_id,
|
||||||
|
)
|
||||||
|
|
||||||
if self.amodule.params['rollback_to'] is not None:
|
if self.amodule.params['rollback_to'] is not None:
|
||||||
self.sdk_checkmode(self.api.cloudapi.compute.snapshot_rollback)(
|
self.sdk_checkmode(self.api.cloudapi.compute.snapshot_rollback)(
|
||||||
vm_id=self.comp_info['id'],
|
vm_id=self.comp_info['id'],
|
||||||
@@ -693,10 +719,16 @@ class decort_vm(DecortController):
|
|||||||
aparam_disk_id = aparam_boot['disk_id']
|
aparam_disk_id = aparam_boot['disk_id']
|
||||||
if aparam_disk_id is not None:
|
if aparam_disk_id is not None:
|
||||||
for disk in self.comp_info['disks']:
|
for disk in self.comp_info['disks']:
|
||||||
if disk['id'] == aparam_disk_id and disk['type'] != 'B':
|
if (
|
||||||
self.compute_boot_disk(
|
disk['id'] == aparam_disk_id
|
||||||
comp_id=self.comp_info['id'],
|
and not self.is_vm_boot_disk(
|
||||||
boot_disk=aparam_disk_id,
|
vm_chipset=self.comp_info['chipset'],
|
||||||
|
vm_disk=disk,
|
||||||
|
)
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.compute.boot_disk_set)(
|
||||||
|
vm_id=self.comp_info['id'],
|
||||||
|
disk_id=aparam_disk_id,
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -734,7 +766,7 @@ class decort_vm(DecortController):
|
|||||||
_, image_facts = self.image_find(
|
_, image_facts = self.image_find(
|
||||||
image_id=self.aparams['image_id'],
|
image_id=self.aparams['image_id'],
|
||||||
)
|
)
|
||||||
disk_size = image_facts['size']
|
disk_size = image_facts['size_gb']
|
||||||
|
|
||||||
os_version = None
|
os_version = None
|
||||||
if (
|
if (
|
||||||
@@ -772,8 +804,8 @@ class decort_vm(DecortController):
|
|||||||
compute_custom_fields = self.comp_info['custom_fields']
|
compute_custom_fields = self.comp_info['custom_fields']
|
||||||
if aparam_custom_fields['disable']:
|
if aparam_custom_fields['disable']:
|
||||||
if compute_custom_fields is not None:
|
if compute_custom_fields is not None:
|
||||||
self.compute_disable_custom_fields(
|
self.sdk_checkmode(self.api.ca.compute.delete_custom_fields)(
|
||||||
compute_id=self.comp_info['id'],
|
vm_id=self.comp_info['id'],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if compute_custom_fields != aparam_custom_fields['fields']:
|
if compute_custom_fields != aparam_custom_fields['fields']:
|
||||||
@@ -784,8 +816,8 @@ class decort_vm(DecortController):
|
|||||||
|
|
||||||
aparam_zone_id = self.aparams['zone_id']
|
aparam_zone_id = self.aparams['zone_id']
|
||||||
if aparam_zone_id is not None and aparam_zone_id != self.comp_info['zoneId']:
|
if aparam_zone_id is not None and aparam_zone_id != self.comp_info['zoneId']:
|
||||||
self.compute_migrate_to_zone(
|
self.sdk_checkmode(self.api.cloudapi.compute.migrate_to_zone)(
|
||||||
compute_id=self.comp_id,
|
vm_id=self.comp_id,
|
||||||
zone_id=aparam_zone_id,
|
zone_id=aparam_zone_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -796,15 +828,21 @@ class decort_vm(DecortController):
|
|||||||
aparam_guest_agent['enabled']
|
aparam_guest_agent['enabled']
|
||||||
and not self.comp_info['qemu_guest']['enabled']
|
and not self.comp_info['qemu_guest']['enabled']
|
||||||
):
|
):
|
||||||
self.compute_guest_agent_enable(vm_id=self.comp_id)
|
self.sdk_checkmode(
|
||||||
|
self.api.cloudapi.compute.guest_agent_enable
|
||||||
|
)(vm_id=self.comp_id)
|
||||||
elif (
|
elif (
|
||||||
aparam_guest_agent['enabled'] is False
|
aparam_guest_agent['enabled'] is False
|
||||||
and self.comp_info['qemu_guest']['enabled']
|
and self.comp_info['qemu_guest']['enabled']
|
||||||
):
|
):
|
||||||
self.compute_guest_agent_disable(vm_id=self.comp_id)
|
self.sdk_checkmode(
|
||||||
|
self.api.cloudapi.compute.guest_agent_disable
|
||||||
|
)(vm_id=self.comp_id)
|
||||||
|
|
||||||
if aparam_guest_agent['update_available_commands']:
|
if aparam_guest_agent['update_available_commands']:
|
||||||
self.compute_guest_agent_feature_update(vm_id=self.comp_id)
|
self.sdk_checkmode(
|
||||||
|
self.api.cloudapi.compute.guest_agent_feature_update
|
||||||
|
)(vm_id=self.comp_id)
|
||||||
|
|
||||||
aparam_guest_agent_exec = aparam_guest_agent['exec']
|
aparam_guest_agent_exec = aparam_guest_agent['exec']
|
||||||
if aparam_guest_agent_exec is not None:
|
if aparam_guest_agent_exec is not None:
|
||||||
@@ -838,6 +876,28 @@ class decort_vm(DecortController):
|
|||||||
vm_id=self.comp_id,
|
vm_id=self.comp_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cpu_alignment_profile = self.aparams['cpu_alignment_profile_name']
|
||||||
|
|
||||||
|
if cpu_alignment_profile:
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
self.comp_info['cpu_alignment_profile']
|
||||||
|
and cpu_alignment_profile != self.comp_info['cpu_alignment_profile']['name']
|
||||||
|
)
|
||||||
|
or not self.comp_info['cpu_alignment_profile']
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.ca.compute.set_cpu_alignment_profile)(
|
||||||
|
vm_ids=[self.comp_id],
|
||||||
|
cpu_alignment_profile_name=self.aparams['cpu_alignment_profile_name'],
|
||||||
|
)
|
||||||
|
elif (
|
||||||
|
cpu_alignment_profile == ''
|
||||||
|
and self.comp_info['cpu_alignment_profile']
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.ca.compute.delete_cpu_alignment_profile)(
|
||||||
|
vm_ids=[self.comp_id],
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -858,6 +918,7 @@ class decort_vm(DecortController):
|
|||||||
'network_interface_naming': 'networkInterfaceNaming',
|
'network_interface_naming': 'networkInterfaceNaming',
|
||||||
'hot_resize': 'hotResize',
|
'hot_resize': 'hotResize',
|
||||||
'os_version': 'os_version',
|
'os_version': 'os_version',
|
||||||
|
'clock_sync_mode': 'clock',
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_nested_value(
|
def get_nested_value(
|
||||||
@@ -934,11 +995,11 @@ class decort_vm(DecortController):
|
|||||||
tech_status="",
|
tech_status="",
|
||||||
account_id=0,
|
account_id=0,
|
||||||
rg_id=0,
|
rg_id=0,
|
||||||
|
folder_id=0,
|
||||||
username="",
|
username="",
|
||||||
password="",
|
password="",
|
||||||
public_ips=[], # direct IPs; this list can be empty
|
public_ips=[], # direct IPs; this list can be empty
|
||||||
private_ips=[], # IPs on ViNSes; usually, at least one IP is listed
|
private_ips=[], # IPs on ViNSes; usually, at least one IP is listed
|
||||||
nat_ip="", # IP of the external ViNS interface; can be empty.
|
|
||||||
tags={},
|
tags={},
|
||||||
chipset="",
|
chipset="",
|
||||||
interfaces=[],
|
interfaces=[],
|
||||||
@@ -968,6 +1029,7 @@ class decort_vm(DecortController):
|
|||||||
ret_dict['tech_status'] = self.comp_info['techStatus']
|
ret_dict['tech_status'] = self.comp_info['techStatus']
|
||||||
ret_dict['account_id'] = self.comp_info['accountId']
|
ret_dict['account_id'] = self.comp_info['accountId']
|
||||||
ret_dict['rg_id'] = self.comp_info['rgId']
|
ret_dict['rg_id'] = self.comp_info['rgId']
|
||||||
|
ret_dict['folder_id'] = self.comp_info.get('folder_id') or 0
|
||||||
if self.comp_info['tags']:
|
if self.comp_info['tags']:
|
||||||
ret_dict['tags'] = self.comp_info['tags']
|
ret_dict['tags'] = self.comp_info['tags']
|
||||||
# if the VM is an imported VM, then the 'accounts' list may be empty,
|
# if the VM is an imported VM, then the 'accounts' list may be empty,
|
||||||
@@ -977,16 +1039,9 @@ class decort_vm(DecortController):
|
|||||||
ret_dict['password'] = self.comp_info['osUsers'][0]['password']
|
ret_dict['password'] = self.comp_info['osUsers'][0]['password']
|
||||||
|
|
||||||
if self.comp_info['interfaces']:
|
if self.comp_info['interfaces']:
|
||||||
# We need a list of all ViNSes in the account, which owns this Compute
|
|
||||||
# to find a ViNS, which may have active external connection. Then
|
|
||||||
# we will save external IP address of that connection in ret_dict['nat_ip']
|
|
||||||
|
|
||||||
for iface in self.comp_info['interfaces']:
|
for iface in self.comp_info['interfaces']:
|
||||||
if iface['connType'] == "VXLAN": # This is ViNS connection
|
if iface['connType'] == "VXLAN": # This is ViNS connection
|
||||||
ret_dict['private_ips'].append(iface['ipAddress'])
|
ret_dict['private_ips'].append(iface['ipAddress'])
|
||||||
# if iface['connId']
|
|
||||||
# Now we need to check if this ViNS has GW function and external connection.
|
|
||||||
# If it does - save public IP address of GW VNF in ret_dict['nat_ip']
|
|
||||||
elif iface['connType'] == "VLAN": # This is direct external network connection
|
elif iface['connType'] == "VLAN": # This is direct external network connection
|
||||||
ret_dict['public_ips'].append(iface['ipAddress'])
|
ret_dict['public_ips'].append(iface['ipAddress'])
|
||||||
|
|
||||||
@@ -1000,10 +1055,17 @@ class decort_vm(DecortController):
|
|||||||
|
|
||||||
ret_dict['disks'] = self.comp_info['disks']
|
ret_dict['disks'] = self.comp_info['disks']
|
||||||
for disk in ret_dict['disks']:
|
for disk in ret_dict['disks']:
|
||||||
if disk['type'] == 'B':
|
disk['read_only_mode'] = disk.pop('read_only')
|
||||||
|
if self.is_vm_boot_disk(
|
||||||
|
vm_chipset=self.comp_info['chipset'],
|
||||||
|
vm_disk=disk,
|
||||||
|
):
|
||||||
# if it is a boot disk - store its size
|
# if it is a boot disk - store its size
|
||||||
ret_dict['disk_size'] = disk['sizeMax']
|
ret_dict['disk_size'] = disk['sizeMax']
|
||||||
|
|
||||||
|
for snapshot in disk['snapshots']:
|
||||||
|
snapshot['pool_name'] = snapshot.pop('poolName')
|
||||||
|
|
||||||
ret_dict['chipset'] = self.comp_info['chipset']
|
ret_dict['chipset'] = self.comp_info['chipset']
|
||||||
|
|
||||||
ret_dict['interfaces'] = self.comp_info['interfaces']
|
ret_dict['interfaces'] = self.comp_info['interfaces']
|
||||||
@@ -1073,6 +1135,14 @@ class decort_vm(DecortController):
|
|||||||
|
|
||||||
ret_dict['read_only'] = self.comp_info['read_only']
|
ret_dict['read_only'] = self.comp_info['read_only']
|
||||||
|
|
||||||
|
ret_dict['weight'] = self.comp_info['weight']
|
||||||
|
|
||||||
|
ret_dict['clock_sync_mode'] = self.comp_info['clock']
|
||||||
|
|
||||||
|
ret_dict['cpu_alignment_profile'] = self.comp_info['cpu_alignment_profile']
|
||||||
|
|
||||||
|
ret_dict['resized_online'] = self.comp_info['resizedOnline']
|
||||||
|
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
def check_amodule_args_for_create(self):
|
def check_amodule_args_for_create(self):
|
||||||
@@ -1160,6 +1230,20 @@ class decort_vm(DecortController):
|
|||||||
|
|
||||||
if self.check_aparam_zone_id() is False:
|
if self.check_aparam_zone_id() is False:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
|
else:
|
||||||
|
if (
|
||||||
|
self.aparams['cpu_alignment_profile_name']
|
||||||
|
and self.aparams['zone_id']
|
||||||
|
):
|
||||||
|
zone_info = self.zone_get(self.aparams['zone_id'])
|
||||||
|
if not zone_info['cpu_alignment_profiles']:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "zone_id" failed:'
|
||||||
|
f' zone ID {self.aparams['zone_id']} must contain'
|
||||||
|
' cpu alignment profiles'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if self.aparams['guest_agent'] is not None:
|
if self.aparams['guest_agent'] is not None:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
@@ -1199,7 +1283,7 @@ class decort_vm(DecortController):
|
|||||||
)
|
)
|
||||||
elif (
|
elif (
|
||||||
aparam_storage_policy_id
|
aparam_storage_policy_id
|
||||||
not in self.rg_info['storage_policy_ids']
|
not in self.rg_info.storage_policy_ids
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
@@ -1215,6 +1299,17 @@ class decort_vm(DecortController):
|
|||||||
'abort_cloning can be specified only for existing compute.'
|
'abort_cloning can be specified only for existing compute.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['cpu_alignment_profile_name'] is not None
|
||||||
|
and self.aparams['custom_fields'] is not None
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "cpu_alignment_profile_name" failed: '
|
||||||
|
'cpu_alignment_profile_name cannot be used together '
|
||||||
|
'with parameter "custom_fields".'
|
||||||
|
)
|
||||||
|
|
||||||
if check_errors:
|
if check_errors:
|
||||||
self.exit(fail=True)
|
self.exit(fail=True)
|
||||||
|
|
||||||
@@ -1312,6 +1407,9 @@ class decort_vm(DecortController):
|
|||||||
bus_num_hex=dict(
|
bus_num_hex=dict(
|
||||||
type='str',
|
type='str',
|
||||||
),
|
),
|
||||||
|
read_only=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
required_together=[
|
required_together=[
|
||||||
('pci_slot_num_hex', 'bus_num_hex'),
|
('pci_slot_num_hex', 'bus_num_hex'),
|
||||||
@@ -1319,6 +1417,9 @@ class decort_vm(DecortController):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
folder_id=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
id=dict(
|
id=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=0,
|
default=0,
|
||||||
@@ -1371,6 +1472,9 @@ class decort_vm(DecortController):
|
|||||||
net_prefix=dict(
|
net_prefix=dict(
|
||||||
type='int',
|
type='int',
|
||||||
),
|
),
|
||||||
|
enable_default_gateway=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
required_if=[
|
required_if=[
|
||||||
('type', 'VINS', ('id',)),
|
('type', 'VINS', ('id',)),
|
||||||
@@ -1587,6 +1691,18 @@ class decort_vm(DecortController):
|
|||||||
abort_cloning=dict(
|
abort_cloning=dict(
|
||||||
type='bool',
|
type='bool',
|
||||||
),
|
),
|
||||||
|
cpu_alignment_profile_name=dict(
|
||||||
|
type='str',
|
||||||
|
),
|
||||||
|
clock_sync_mode=dict(
|
||||||
|
type='str',
|
||||||
|
choices=[
|
||||||
|
'default',
|
||||||
|
'linux',
|
||||||
|
'windows',
|
||||||
|
'none',
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
required_one_of=[
|
required_one_of=[
|
||||||
@@ -1637,12 +1753,17 @@ class decort_vm(DecortController):
|
|||||||
f'disk {aparam_boot_disk_id} is not attached to '
|
f'disk {aparam_boot_disk_id} is not attached to '
|
||||||
f'Compute ID {self.comp_id}.'
|
f'Compute ID {self.comp_id}.'
|
||||||
)
|
)
|
||||||
else:
|
elif aparam_boot_disk_id is not None:
|
||||||
|
aparam_disk_ids = []
|
||||||
|
if aparam_disks['objects']:
|
||||||
|
aparam_disk_ids = [
|
||||||
|
disk['id'] for disk in aparam_disks['objects']
|
||||||
|
]
|
||||||
match aparam_disks['mode']:
|
match aparam_disks['mode']:
|
||||||
case 'update':
|
case 'update':
|
||||||
if (
|
if (
|
||||||
aparam_boot_disk_id not in comp_disk_ids
|
aparam_boot_disk_id not in comp_disk_ids
|
||||||
and aparam_boot_disk_id not in aparam_disks['ids']
|
and aparam_boot_disk_id not in aparam_disk_ids
|
||||||
):
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
@@ -1651,7 +1772,7 @@ class decort_vm(DecortController):
|
|||||||
f'to Compute ID {self.comp_id}.'
|
f'to Compute ID {self.comp_id}.'
|
||||||
)
|
)
|
||||||
case 'match':
|
case 'match':
|
||||||
if aparam_boot_disk_id not in aparam_disks['ids']:
|
if aparam_boot_disk_id not in aparam_disk_ids:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
f'Check for parameter "boot.disk_id" failed: '
|
f'Check for parameter "boot.disk_id" failed: '
|
||||||
@@ -1659,7 +1780,7 @@ class decort_vm(DecortController):
|
|||||||
f'disks.ids'
|
f'disks.ids'
|
||||||
)
|
)
|
||||||
case 'detach' | 'delete':
|
case 'detach' | 'delete':
|
||||||
if aparam_boot_disk_id in aparam_disks['ids']:
|
if aparam_boot_disk_id in aparam_disk_ids:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
f'Check for parameter "boot.disk_id" failed: '
|
f'Check for parameter "boot.disk_id" failed: '
|
||||||
@@ -1681,7 +1802,9 @@ class decort_vm(DecortController):
|
|||||||
if new_boot_disk_size is not None:
|
if new_boot_disk_size is not None:
|
||||||
boot_disk_size = 0
|
boot_disk_size = 0
|
||||||
for disk in self.comp_info['disks']:
|
for disk in self.comp_info['disks']:
|
||||||
if disk['type'] == 'B':
|
if self.is_vm_boot_disk(
|
||||||
|
vm_chipset=self.comp_info['chipset'], vm_disk=disk,
|
||||||
|
):
|
||||||
boot_disk_size = disk['sizeMax']
|
boot_disk_size = disk['sizeMax']
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@@ -1844,7 +1967,9 @@ class decort_vm(DecortController):
|
|||||||
aparam_disks_ids = [disk['id'] for disk in aparam_disks]
|
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 self.is_vm_boot_disk(
|
||||||
|
vm_chipset=self.comp_info['chipset'], vm_disk=comp_disk,
|
||||||
|
):
|
||||||
comp_boot_disk_id = comp_disk['id']
|
comp_boot_disk_id = comp_disk['id']
|
||||||
break
|
break
|
||||||
disks_to_detach = []
|
disks_to_detach = []
|
||||||
@@ -1904,6 +2029,22 @@ class decort_vm(DecortController):
|
|||||||
|
|
||||||
if self.check_aparam_zone_id() is False:
|
if self.check_aparam_zone_id() is False:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
|
else:
|
||||||
|
if (
|
||||||
|
self.aparams['zone_id']
|
||||||
|
and self.aparams['zone_id'] != self.comp_info['zoneId']
|
||||||
|
):
|
||||||
|
zone_info = self.zone_get(self.aparams['zone_id'])
|
||||||
|
if (
|
||||||
|
not zone_info['cpu_alignment_profiles']
|
||||||
|
and self.comp_info['cpu_alignment_profile']
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "zone_id" failed: '
|
||||||
|
f'zone ID {self.aparams['zone_id']} '
|
||||||
|
'must contain cpu alignment profiles.'
|
||||||
|
)
|
||||||
|
|
||||||
if self.check_aparam_guest_agent() is False:
|
if self.check_aparam_guest_agent() is False:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
@@ -1969,6 +2110,18 @@ class decort_vm(DecortController):
|
|||||||
'Only networks of type VINS, EXTNET, DPDK, SDN, TRUNK '
|
'Only networks of type VINS, EXTNET, DPDK, SDN, TRUNK '
|
||||||
'can be enabled or disabled.'
|
'can be enabled or disabled.'
|
||||||
)
|
)
|
||||||
|
if (
|
||||||
|
network['enable_default_gateway'] is not None
|
||||||
|
and network['type'] != self.VMNetType.VINS.value
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter '
|
||||||
|
'"networks.enable_default_gateway" failed: '
|
||||||
|
'Can not enable or disable default gateway for network'
|
||||||
|
f' ID {network['id']} and type {network['type']}. '
|
||||||
|
'Only networks of type VINS can have default gateway.'
|
||||||
|
)
|
||||||
|
|
||||||
if self.check_aparam_cdrom() is False:
|
if self.check_aparam_cdrom() is False:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
@@ -1979,6 +2132,27 @@ class decort_vm(DecortController):
|
|||||||
if self.check_aparam_image_id() is False:
|
if self.check_aparam_image_id() is False:
|
||||||
check_errors = True
|
check_errors = True
|
||||||
|
|
||||||
|
if self.aparams['cpu_alignment_profile_name'] is not None:
|
||||||
|
if self.comp_info['snapSets'] or self.comp_info['custom_fields']:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "cpu_alignment_profile_name" failed: '
|
||||||
|
'VM should not contain snapshots or custom fields.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['boot']
|
||||||
|
and self.aparams['boot']['disk_redeploy'] is not None
|
||||||
|
and self.aparams['image_id'] is not None
|
||||||
|
):
|
||||||
|
if self.aparams['storage_policy_id'] is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
f'Check for parameter "storage_policy_id" failed: '
|
||||||
|
'storage_policy_id must be specified when changing '
|
||||||
|
'the OS image'
|
||||||
|
)
|
||||||
|
|
||||||
if check_errors:
|
if check_errors:
|
||||||
self.exit(fail=True)
|
self.exit(fail=True)
|
||||||
|
|
||||||
@@ -2137,8 +2311,8 @@ class decort_vm(DecortController):
|
|||||||
vm_has_shared_sep_disk = False
|
vm_has_shared_sep_disk = False
|
||||||
vm_disk_ids = [disk['id'] for disk in self.comp_info['disks']]
|
vm_disk_ids = [disk['id'] for disk in self.comp_info['disks']]
|
||||||
for disk_id in vm_disk_ids:
|
for disk_id in vm_disk_ids:
|
||||||
_, disk_info = self._disk_get_by_id(disk_id=disk_id)
|
disk_info = self._disk_get_by_id(disk_id=disk_id)
|
||||||
if disk_info['sepType'] == 'SHARED':
|
if disk_info.sep_type == sdk_types.SEPType.SHARED:
|
||||||
vm_has_shared_sep_disk = True
|
vm_has_shared_sep_disk = True
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -2230,7 +2404,9 @@ class decort_vm(DecortController):
|
|||||||
if disk_redeploy:
|
if disk_redeploy:
|
||||||
vm_has_boot_disk = False
|
vm_has_boot_disk = False
|
||||||
for disk in self.comp_info['disks']:
|
for disk in self.comp_info['disks']:
|
||||||
if disk['type'] == 'B':
|
if self.is_vm_boot_disk(
|
||||||
|
vm_chipset=self.comp_info['chipset'], vm_disk=disk,
|
||||||
|
):
|
||||||
vm_has_boot_disk = True
|
vm_has_boot_disk = True
|
||||||
break
|
break
|
||||||
if not vm_has_boot_disk:
|
if not vm_has_boot_disk:
|
||||||
@@ -2341,7 +2517,9 @@ class decort_vm(DecortController):
|
|||||||
check_errors = False
|
check_errors = False
|
||||||
|
|
||||||
# check if account has vm feature “trunk”
|
# check if account has vm feature “trunk”
|
||||||
if not self.check_account_vm_features(vm_feature=self.VMFeature.trunk):
|
if not self.check_account_vm_features(
|
||||||
|
vm_feature=sdk_types.VMFeature.TRUNK,
|
||||||
|
):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
'Check for parameter "networks" failed: '
|
'Check for parameter "networks" failed: '
|
||||||
@@ -2349,7 +2527,7 @@ class decort_vm(DecortController):
|
|||||||
'trunk type networks '
|
'trunk type networks '
|
||||||
)
|
)
|
||||||
# check if rg has vm feature “trunk”
|
# check if rg has vm feature “trunk”
|
||||||
if not self.check_rg_vm_features(vm_feature=self.VMFeature.trunk):
|
if not self.check_rg_vm_features(vm_feature=sdk_types.VMFeature.TRUNK):
|
||||||
check_errors = True
|
check_errors = True
|
||||||
self.message(
|
self.message(
|
||||||
'Check for parameter "networks" failed: '
|
'Check for parameter "networks" failed: '
|
||||||
|
|||||||
235
library/decort_vm_folder.py
Normal file
235
library/decort_vm_folder.py
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_vm_folder
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import (
|
||||||
|
AnsibleModuleParamSet,
|
||||||
|
DecortController,
|
||||||
|
)
|
||||||
|
|
||||||
|
from dynamix_sdk import exceptions as sdk_exceptions
|
||||||
|
from dynamix_sdk import types as sdk_types
|
||||||
|
|
||||||
|
|
||||||
|
class VmFolderState(Enum):
|
||||||
|
absent = 'absent'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ParamSet(AnsibleModuleParamSet):
|
||||||
|
id: int | None
|
||||||
|
name: str | None
|
||||||
|
parent_id: int | None
|
||||||
|
rg_id: int | None
|
||||||
|
state: VmFolderState | None
|
||||||
|
|
||||||
|
|
||||||
|
class DecortVmFolder(DecortController):
|
||||||
|
param_set: ParamSet
|
||||||
|
facts: dict[str, Any]
|
||||||
|
_id: int | None = None
|
||||||
|
_vm_folder_info: sdk_types.CloudapiVmfolderGetResultModel | None = None
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
self.param_set = ParamSet.from_dict(self.amodule.params)
|
||||||
|
self.facts = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict[str, Any]:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=ParamSet.get_arg_spec(),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self) -> int:
|
||||||
|
if self._id is None:
|
||||||
|
raise TypeError
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vm_folder_info(self) -> sdk_types.CloudapiVmfolderGetResultModel:
|
||||||
|
if self._vm_folder_info is None:
|
||||||
|
try:
|
||||||
|
vm_folder_info = self.api.cloudapi.vmfolder.get(
|
||||||
|
folder_id=self.id,
|
||||||
|
)
|
||||||
|
except sdk_exceptions.RequestException as e:
|
||||||
|
if (
|
||||||
|
e.orig_exception.response is not None
|
||||||
|
and e.orig_exception.response.status_code == 404
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='vm_folder',
|
||||||
|
id=self.id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
raise e
|
||||||
|
self._vm_folder_info = vm_folder_info
|
||||||
|
return self._vm_folder_info
|
||||||
|
|
||||||
|
@DecortController.handle_sdk_exceptions
|
||||||
|
def run(self) -> None:
|
||||||
|
if self.param_set.id is not None:
|
||||||
|
self._id = self.param_set.id
|
||||||
|
elif self.param_set.name is not None:
|
||||||
|
self._id = self.find_id_by_name()
|
||||||
|
|
||||||
|
if self._id is not None:
|
||||||
|
self.get_info()
|
||||||
|
self.change()
|
||||||
|
else:
|
||||||
|
self.check_amodule_args_for_create()
|
||||||
|
self.create()
|
||||||
|
|
||||||
|
if self.result['changed']:
|
||||||
|
self.get_info()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def find_id_by_name(self) -> int | None:
|
||||||
|
rg_id = self.param_set.rg_id
|
||||||
|
name = self.param_set.name
|
||||||
|
if rg_id is None or name is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
matches: list[int] = []
|
||||||
|
page = 1
|
||||||
|
page_size = 100
|
||||||
|
while True:
|
||||||
|
folders = self.api.cloudapi.vmfolder.list(
|
||||||
|
rg_id=rg_id,
|
||||||
|
page=page,
|
||||||
|
size=page_size,
|
||||||
|
)
|
||||||
|
for folder in folders.data:
|
||||||
|
if folder.name != name:
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
self.param_set.parent_id is not None
|
||||||
|
and folder.parent_id != self.param_set.parent_id
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
matches.append(folder.id)
|
||||||
|
|
||||||
|
if not folders.data or len(folders.data) < page_size:
|
||||||
|
break
|
||||||
|
if (
|
||||||
|
folders.entry_count
|
||||||
|
and page * page_size >= folders.entry_count
|
||||||
|
):
|
||||||
|
break
|
||||||
|
page += 1
|
||||||
|
|
||||||
|
if len(matches) > 1:
|
||||||
|
self.message(
|
||||||
|
msg='Check for parameter "name" failed: '
|
||||||
|
'multiple vm folders with this name were found; '
|
||||||
|
'specify parent_id or id'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
if matches:
|
||||||
|
return matches[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_info(self) -> None:
|
||||||
|
if self._vm_folder_info is not None and not self.amodule.check_mode:
|
||||||
|
self._vm_folder_info = None
|
||||||
|
self.facts = self.vm_folder_info.model_dump()
|
||||||
|
|
||||||
|
def check_amodule_args_for_create(self) -> None:
|
||||||
|
check_errors = False
|
||||||
|
|
||||||
|
if self.param_set.rg_id is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
msg='Check for parameter "rg_id" failed: '
|
||||||
|
'rg_id must be specified when creating '
|
||||||
|
'a new vm_folder'
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.param_set.name is None:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
msg='Check for parameter "name" failed: '
|
||||||
|
'name must be specified when creating '
|
||||||
|
'a new vm_folder'
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.param_set.state == VmFolderState.absent:
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
msg='Check for parameter "state" failed: '
|
||||||
|
'state can not be "absent" when creating '
|
||||||
|
'a new vm_folder'
|
||||||
|
)
|
||||||
|
|
||||||
|
if check_errors:
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
def create(self) -> None:
|
||||||
|
rg_id = self.param_set.rg_id
|
||||||
|
name = self.param_set.name
|
||||||
|
if rg_id is None or name is None:
|
||||||
|
raise TypeError
|
||||||
|
created_id = self.sdk_checkmode(
|
||||||
|
self.api.cloudapi.vmfolder.create
|
||||||
|
)(
|
||||||
|
rg_id=rg_id,
|
||||||
|
name=name,
|
||||||
|
parent_id=self.param_set.parent_id,
|
||||||
|
)
|
||||||
|
if created_id:
|
||||||
|
self._id = created_id
|
||||||
|
|
||||||
|
def change(self) -> None:
|
||||||
|
self.change_state()
|
||||||
|
self.change_params()
|
||||||
|
|
||||||
|
def change_state(self) -> None:
|
||||||
|
if self.param_set.state == VmFolderState.absent:
|
||||||
|
self.delete()
|
||||||
|
|
||||||
|
def change_params(self) -> None:
|
||||||
|
if (
|
||||||
|
self.param_set.name is not None
|
||||||
|
and self.param_set.name != self.vm_folder_info.name
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.vmfolder.rename)(
|
||||||
|
folder_id=self.id,
|
||||||
|
name=self.param_set.name,
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
self.param_set.parent_id is not None
|
||||||
|
and self.param_set.parent_id != self.vm_folder_info.parent_id
|
||||||
|
):
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.vmfolder.move)(
|
||||||
|
folder_id=self.id,
|
||||||
|
new_parent_id=self.param_set.parent_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self) -> None:
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.vmfolder.delete)(
|
||||||
|
folder_id=self.id,
|
||||||
|
)
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
DecortVmFolder().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
141
library/decort_vm_folder_access.py
Normal file
141
library/decort_vm_folder_access.py
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_vm_folder_access
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import (
|
||||||
|
AnsibleModuleParamSet,
|
||||||
|
DecortController,
|
||||||
|
)
|
||||||
|
|
||||||
|
from dynamix_sdk import exceptions as sdk_exceptions
|
||||||
|
from dynamix_sdk import types as sdk_types
|
||||||
|
|
||||||
|
|
||||||
|
class VMFolderAccessState(Enum):
|
||||||
|
absent = 'absent'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ParamSet(AnsibleModuleParamSet):
|
||||||
|
folder_id: int
|
||||||
|
user: str
|
||||||
|
right: sdk_types.AccessTypeForSet | None
|
||||||
|
state: VMFolderAccessState | None
|
||||||
|
|
||||||
|
|
||||||
|
class DecortVMFolderAccess(DecortController):
|
||||||
|
param_set: ParamSet
|
||||||
|
folder_info: sdk_types.CloudapiVmfolderGetResultModel
|
||||||
|
acl: sdk_types.ACLForVMFolderGetAPIResultNM | None = None
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
self.param_set = ParamSet.from_dict(self.amodule.params)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict[str, Any]:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=ParamSet.get_arg_spec(),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@DecortController.handle_sdk_exceptions
|
||||||
|
def run(self) -> None:
|
||||||
|
self.get_folder()
|
||||||
|
self.check_amodule_args()
|
||||||
|
self.change()
|
||||||
|
if self.result['changed'] and not self.amodule.check_mode:
|
||||||
|
self.get_folder()
|
||||||
|
self.package_facts()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_folder(self) -> None:
|
||||||
|
try:
|
||||||
|
self.folder_info = self.api.cloudapi.vmfolder.get(
|
||||||
|
folder_id=self.param_set.folder_id,
|
||||||
|
)
|
||||||
|
except sdk_exceptions.RequestException as e:
|
||||||
|
if (
|
||||||
|
e.orig_exception.response is not None
|
||||||
|
and e.orig_exception.response.status_code == 404
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='VM folder',
|
||||||
|
id=self.param_set.folder_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
raise e
|
||||||
|
self.acl = self.find_acl()
|
||||||
|
|
||||||
|
def find_acl(self) -> sdk_types.ACLForVMFolderGetAPIResultNM | None:
|
||||||
|
for acl in self.folder_info.acl:
|
||||||
|
if acl.explicit and acl.user_group_id == self.param_set.user:
|
||||||
|
return acl
|
||||||
|
return None
|
||||||
|
|
||||||
|
def check_amodule_args(self) -> None:
|
||||||
|
if (
|
||||||
|
self.param_set.state is None
|
||||||
|
and self.param_set.right is None
|
||||||
|
and self.acl is None
|
||||||
|
):
|
||||||
|
self.message(
|
||||||
|
self.MESSAGES.obj_not_found(
|
||||||
|
obj='VM folder ACL',
|
||||||
|
id=self.param_set.user,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
def change(self) -> None:
|
||||||
|
if self.param_set.state == VMFolderAccessState.absent:
|
||||||
|
self.delete()
|
||||||
|
return
|
||||||
|
|
||||||
|
right = self.param_set.right
|
||||||
|
if right is None or (
|
||||||
|
self.acl is not None and self.acl.right == right
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.vmfolder.add_acl_access)(
|
||||||
|
folder_id=self.param_set.folder_id,
|
||||||
|
user=self.param_set.user,
|
||||||
|
right=right,
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self) -> None:
|
||||||
|
if self.acl is None:
|
||||||
|
return
|
||||||
|
self.sdk_checkmode(self.api.cloudapi.vmfolder.del_acl_access)(
|
||||||
|
folder_id=self.param_set.folder_id,
|
||||||
|
user=self.param_set.user,
|
||||||
|
)
|
||||||
|
if not self.amodule.check_mode:
|
||||||
|
self.acl = None
|
||||||
|
|
||||||
|
def package_facts(self) -> None:
|
||||||
|
if self.acl is not None:
|
||||||
|
self.facts = self.acl.model_dump()
|
||||||
|
else:
|
||||||
|
self.facts = {}
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
DecortVMFolderAccess().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
71
library/decort_vm_folder_list.py
Normal file
71
library/decort_vm_folder_list.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: decort_vm_folder_list
|
||||||
|
|
||||||
|
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||||
|
'''
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.decort_utils import (
|
||||||
|
AnsibleModuleParamSet,
|
||||||
|
DecortController,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FilterParam:
|
||||||
|
rg_id: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class PaginationParam:
|
||||||
|
page: int | None
|
||||||
|
size: int | None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ParamSet(AnsibleModuleParamSet):
|
||||||
|
filter: FilterParam
|
||||||
|
pagination: PaginationParam
|
||||||
|
|
||||||
|
|
||||||
|
class DecortVMFolderList(DecortController):
|
||||||
|
param_set: ParamSet
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||||
|
self.param_set = ParamSet.from_dict(self.amodule.params)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def amodule_init_args(self) -> dict:
|
||||||
|
return self.pack_amodule_init_args(
|
||||||
|
argument_spec=ParamSet.get_arg_spec(),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@DecortController.handle_sdk_exceptions
|
||||||
|
def run(self):
|
||||||
|
self.get_info()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
aparam_filter = self.param_set.filter
|
||||||
|
aparam_pagination = self.param_set.pagination
|
||||||
|
|
||||||
|
self.facts = self.api.cloudapi.vmfolder.list(
|
||||||
|
rg_id=aparam_filter.rg_id,
|
||||||
|
page=aparam_pagination.page,
|
||||||
|
size=aparam_pagination.size,
|
||||||
|
).model_dump()['data']
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DecortVMFolderList().run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -36,12 +36,18 @@ class DecortVMList(DecortController):
|
|||||||
ext_net_name=dict(
|
ext_net_name=dict(
|
||||||
type='str',
|
type='str',
|
||||||
),
|
),
|
||||||
|
folder_id=dict(
|
||||||
|
type='int',
|
||||||
|
),
|
||||||
id=dict(
|
id=dict(
|
||||||
type='int',
|
type='int',
|
||||||
),
|
),
|
||||||
include_deleted=dict(
|
include_deleted=dict(
|
||||||
type='bool',
|
type='bool',
|
||||||
),
|
),
|
||||||
|
include_descendants=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
ip_addr=dict(
|
ip_addr=dict(
|
||||||
type='str',
|
type='str',
|
||||||
),
|
),
|
||||||
@@ -77,7 +83,7 @@ class DecortVMList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -129,8 +135,10 @@ class DecortVMList(DecortController):
|
|||||||
account_id=aparam_filter['account_id'],
|
account_id=aparam_filter['account_id'],
|
||||||
ext_net_id=aparam_filter['ext_net_id'],
|
ext_net_id=aparam_filter['ext_net_id'],
|
||||||
ext_net_name=aparam_filter['ext_net_name'],
|
ext_net_name=aparam_filter['ext_net_name'],
|
||||||
|
folder_id=aparam_filter['folder_id'],
|
||||||
id=aparam_filter['id'],
|
id=aparam_filter['id'],
|
||||||
include_deleted=aparam_filter['include_deleted'] or False,
|
include_deleted=aparam_filter['include_deleted'] or False,
|
||||||
|
include_descendants=aparam_filter['include_descendants'] or False,
|
||||||
ip_addr=aparam_filter['ip_addr'],
|
ip_addr=aparam_filter['ip_addr'],
|
||||||
name=aparam_filter['name'],
|
name=aparam_filter['name'],
|
||||||
rg_id=aparam_filter['rg_id'],
|
rg_id=aparam_filter['rg_id'],
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ description: See L(Module Documentation,https://repository.basistech.ru/BASIS/de
|
|||||||
import time
|
import time
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.decort_utils import DecortController
|
from ansible.module_utils.decort_utils import DecortController
|
||||||
|
import dynamix_sdk.types as sdk_types
|
||||||
|
|
||||||
|
|
||||||
class DecortVMSnapshot(DecortController):
|
class DecortVMSnapshot(DecortController):
|
||||||
@@ -21,6 +22,7 @@ class DecortVMSnapshot(DecortController):
|
|||||||
self.vm_facts: dict
|
self.vm_facts: dict
|
||||||
self.aparams_label = self.aparams['label']
|
self.aparams_label = self.aparams['label']
|
||||||
self.aparams_vm_id = self.aparams['vm_id']
|
self.aparams_vm_id = self.aparams['vm_id']
|
||||||
|
self.vm_snapshot: sdk_types.SnapshotSetAPIResultNM | None = None
|
||||||
|
|
||||||
self.vm_id, self.vm_facts, _ = self._compute_get_by_id(
|
self.vm_id, self.vm_facts, _ = self._compute_get_by_id(
|
||||||
comp_id=self.aparams_vm_id,
|
comp_id=self.aparams_vm_id,
|
||||||
@@ -50,11 +52,14 @@ class DecortVMSnapshot(DecortController):
|
|||||||
and self.aparams_label is not None
|
and self.aparams_label is not None
|
||||||
and self.aparams_label not in self.vm_snapshot_labels
|
and self.aparams_label not in self.vm_snapshot_labels
|
||||||
):
|
):
|
||||||
self.message(
|
if self.aparams['state'] == 'absent':
|
||||||
f'Snapshot {self.aparams_label} '
|
self.exit()
|
||||||
f'not found for VM {self.aparams_vm_id}'
|
else:
|
||||||
)
|
self.message(
|
||||||
self.exit(fail=True)
|
f'Snapshot {self.aparams_label} '
|
||||||
|
f'not found for VM {self.aparams_vm_id}'
|
||||||
|
)
|
||||||
|
self.exit(fail=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amodule_init_args(self) -> dict:
|
def amodule_init_args(self) -> dict:
|
||||||
@@ -79,6 +84,9 @@ class DecortVMSnapshot(DecortController):
|
|||||||
type='int',
|
type='int',
|
||||||
required=True,
|
required=True,
|
||||||
),
|
),
|
||||||
|
with_memory=dict(
|
||||||
|
type='bool',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
required_if=[
|
required_if=[
|
||||||
@@ -119,6 +127,7 @@ class DecortVMSnapshot(DecortController):
|
|||||||
label = self.new_snapshot_label or self.aparams_label
|
label = self.new_snapshot_label or self.aparams_label
|
||||||
for snapshot in self.vm_snapshots:
|
for snapshot in self.vm_snapshots:
|
||||||
if snapshot.label == label:
|
if snapshot.label == label:
|
||||||
|
self.vm_snapshot = snapshot
|
||||||
self.facts = snapshot.model_dump()
|
self.facts = snapshot.model_dump()
|
||||||
if self.aparams['usage']:
|
if self.aparams['usage']:
|
||||||
self.facts['stored'] = self.get_snapshot_usage()
|
self.facts['stored'] = self.get_snapshot_usage()
|
||||||
@@ -137,9 +146,22 @@ class DecortVMSnapshot(DecortController):
|
|||||||
self.abort_merge()
|
self.abort_merge()
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
|
with_memory = self.aparams['with_memory']
|
||||||
|
|
||||||
|
if with_memory is None:
|
||||||
|
with_memory = False
|
||||||
|
self.message(
|
||||||
|
msg=self.MESSAGES.default_value_used(
|
||||||
|
param_name='with_memory',
|
||||||
|
default_value=with_memory
|
||||||
|
),
|
||||||
|
warning=True,
|
||||||
|
)
|
||||||
|
|
||||||
self.sdk_checkmode(self.api.cloudapi.compute.snapshot_create)(
|
self.sdk_checkmode(self.api.cloudapi.compute.snapshot_create)(
|
||||||
vm_id=self.aparams_vm_id,
|
vm_id=self.aparams_vm_id,
|
||||||
label=self.new_snapshot_label,
|
label=self.new_snapshot_label,
|
||||||
|
with_memory=with_memory,
|
||||||
)
|
)
|
||||||
self.get_info(update_vm_snapshots=True)
|
self.get_info(update_vm_snapshots=True)
|
||||||
|
|
||||||
@@ -151,10 +173,16 @@ class DecortVMSnapshot(DecortController):
|
|||||||
self.facts = {}
|
self.facts = {}
|
||||||
|
|
||||||
def abort_merge(self):
|
def abort_merge(self):
|
||||||
self.snapshot_abort_merge(
|
self.sdk_checkmode(
|
||||||
|
self.api.cloudapi.compute.abort_shared_snapshot_merge
|
||||||
|
)(
|
||||||
vm_id=self.aparams_vm_id,
|
vm_id=self.aparams_vm_id,
|
||||||
label=self.aparams_label,
|
label=self.aparams_label,
|
||||||
)
|
)
|
||||||
|
self.message(
|
||||||
|
f'Merge aborted for snapshot {self.aparams_label} of VM ID '
|
||||||
|
f'{self.aparams_vm_id}.'
|
||||||
|
)
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
|
||||||
def get_snapshot_usage(self) -> int:
|
def get_snapshot_usage(self) -> int:
|
||||||
@@ -178,7 +206,33 @@ class DecortVMSnapshot(DecortController):
|
|||||||
'Merge can be aborted only for VM in "MERGE" tech status.'
|
'Merge can be aborted only for VM in "MERGE" tech status.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.aparams['with_memory'] is not None
|
||||||
|
and self.vm_snapshot
|
||||||
|
):
|
||||||
|
if self.aparams['with_memory'] != bool(
|
||||||
|
self.vm_snapshot.memory_dump_image_id
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "with_memory" failed: '
|
||||||
|
'Changing the "with_memory" parameter is not '
|
||||||
|
'allowed for an existing snapshot.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.vm_facts['techStatus'] != 'STARTED'
|
||||||
|
and self.aparams['with_memory']
|
||||||
|
):
|
||||||
|
check_errors = True
|
||||||
|
self.message(
|
||||||
|
'Check for parameter "with_memory" failed: '
|
||||||
|
f'VM ID {self.aparams_vm_id} must be started '
|
||||||
|
'to create snapshot with memory dump.'
|
||||||
|
)
|
||||||
|
|
||||||
if check_errors:
|
if check_errors:
|
||||||
|
self.facts = {}
|
||||||
self.exit(fail=True)
|
self.exit(fail=True)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class DecortZone(DecortController):
|
|||||||
zone_model = self.api.cloudapi.zone.get(id=self.id)
|
zone_model = self.api.cloudapi.zone.get(id=self.id)
|
||||||
except sdk_exceptions.RequestException as e:
|
except sdk_exceptions.RequestException as e:
|
||||||
if (
|
if (
|
||||||
e.orig_exception.response
|
e.orig_exception.response is not None
|
||||||
and e.orig_exception.response.status_code == 404
|
and e.orig_exception.response.status_code == 404
|
||||||
):
|
):
|
||||||
self.message(
|
self.message(
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class DecortZoneList(DecortController):
|
|||||||
),
|
),
|
||||||
size=dict(
|
size=dict(
|
||||||
type='int',
|
type='int',
|
||||||
default=50,
|
default=100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
|||||||
ansible==11.6.0
|
ansible==11.6.0
|
||||||
|
dacite==1.9.2
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@1.4.latest
|
git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@2.0.latest
|
||||||
|
|||||||
Reference in New Issue
Block a user