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 |
|
||||
|:----------------:|:--------------------------:|
|
||||
| 4.7.0 | 13.0.x |
|
||||
| 4.6.0 | 12.0.x |
|
||||
| 4.5.0 | 11.0.x |
|
||||
| 4.4.0 | 10.0.x |
|
||||
| 4.4.0 build 963 | 9.0.x |
|
||||
|
||||
@@ -7,9 +7,9 @@ module: decort_account
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Iterable
|
||||
from typing import Any, Iterable
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
from ansible.module_utils.decort_utils import DecortController, sdk_types
|
||||
|
||||
|
||||
class DecortAccount(DecortController):
|
||||
@@ -23,7 +23,7 @@ class DecortAccount(DecortController):
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
access_emails=dict(
|
||||
send_access_emails=dict(
|
||||
type='bool',
|
||||
),
|
||||
acl=dict(
|
||||
@@ -45,8 +45,13 @@ class DecortAccount(DecortController):
|
||||
options=dict(
|
||||
rights=dict(
|
||||
type='str',
|
||||
choices=['R', 'RCX', 'ARCXDU'],
|
||||
default='R',
|
||||
choices=(
|
||||
sdk_types.AccessTypeForSet
|
||||
._member_names_
|
||||
),
|
||||
default=(
|
||||
sdk_types.AccessTypeForSet.R.name
|
||||
),
|
||||
),
|
||||
id=dict(
|
||||
type='str',
|
||||
@@ -69,19 +74,19 @@ class DecortAccount(DecortController):
|
||||
quotas=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
cpu=dict(
|
||||
cpu_count=dict(
|
||||
type='int',
|
||||
),
|
||||
disks_size=dict(
|
||||
storage_size_gb=dict(
|
||||
type='int',
|
||||
),
|
||||
gpu=dict(
|
||||
gpu_count=dict(
|
||||
type='int',
|
||||
),
|
||||
public_ip=dict(
|
||||
ext_ip_count=dict(
|
||||
type='int',
|
||||
),
|
||||
ram=dict(
|
||||
ram_size_mb=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
@@ -135,7 +140,7 @@ class DecortAccount(DecortController):
|
||||
# Parameters or combinations of parameters that can
|
||||
# cause changing the object.
|
||||
changing_params = [
|
||||
'access_emails',
|
||||
'send_access_emails',
|
||||
'acl',
|
||||
['id', 'name'],
|
||||
'quotas',
|
||||
@@ -188,110 +193,308 @@ class DecortAccount(DecortController):
|
||||
self.acc_id, self._acc_info = self.account_find(
|
||||
account_name=self.aparams['name'],
|
||||
account_id=self.aparams['id'],
|
||||
resource_consumption=self.aparams['get_resource_consumption'],
|
||||
)
|
||||
)
|
||||
# If this is a repeated getting info
|
||||
else:
|
||||
# If check mode is enabled, there is no needed to
|
||||
# request info again
|
||||
if not self.amodule.check_mode:
|
||||
self.acc_id, self._acc_info = self.account_find(
|
||||
account_id=self.acc_id,
|
||||
resource_consumption=(
|
||||
self.aparams['get_resource_consumption']
|
||||
),
|
||||
account_id=self._acc_info.id,
|
||||
)
|
||||
self.facts = self.acc_info
|
||||
|
||||
if self._acc_info is not None:
|
||||
acc_info_dict = self.acc_info.model_dump()
|
||||
if self.aparams['get_resource_consumption']:
|
||||
resource_consumption_dict = (
|
||||
self.api.cloudapi.account.get_resource_consumption(
|
||||
account_id=self.acc_info.id,
|
||||
).model_dump()
|
||||
)
|
||||
resource_consumption_dict.pop('id')
|
||||
resource_consumption_dict.pop('quotas')
|
||||
acc_info_dict['resource_consumption'] = (
|
||||
resource_consumption_dict
|
||||
)
|
||||
self.facts = acc_info_dict
|
||||
|
||||
def change(self):
|
||||
self.change_state()
|
||||
|
||||
self.change_acl()
|
||||
|
||||
if self.account_update_args:
|
||||
self.account_update(account_id=self.acc_id,
|
||||
**self.account_update_args)
|
||||
need_account_update_api_call: bool = False
|
||||
|
||||
sdk_param_send_access_emails = None
|
||||
aparam_send_access_emails: bool | None = self.aparams[
|
||||
'send_access_emails'
|
||||
]
|
||||
if (
|
||||
aparam_send_access_emails is not None
|
||||
and self.acc_info.send_access_emails != aparam_send_access_emails
|
||||
):
|
||||
sdk_param_send_access_emails = aparam_send_access_emails
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_name = None
|
||||
aparam_name: str | None = self.aparams['name']
|
||||
if (
|
||||
self.aparams['id'] and aparam_name
|
||||
and self.acc_info.name != aparam_name
|
||||
):
|
||||
sdk_param_name = aparam_name
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_cpu_count_quota = None
|
||||
sdk_param_storage_size_quota_gb = None
|
||||
sdk_param_gpu_count_quota = None
|
||||
sdk_param_ext_ip_count_quota = None
|
||||
sdk_param_ram_size_quota_mb = None
|
||||
aparam_quotas: dict[str, Any] = self.aparams['quotas']
|
||||
if aparam_quotas:
|
||||
aparam_quotas_cpu_count: int | None = aparam_quotas['cpu_count']
|
||||
if (
|
||||
aparam_quotas_cpu_count is not None
|
||||
and self.acc_info.quotas.cpu_count != aparam_quotas_cpu_count
|
||||
):
|
||||
sdk_param_cpu_count_quota = aparam_quotas_cpu_count
|
||||
need_account_update_api_call = True
|
||||
|
||||
aparam_quotas_storage_size_gb: int | None = aparam_quotas[
|
||||
'storage_size_gb'
|
||||
]
|
||||
if (
|
||||
aparam_quotas_storage_size_gb is not None
|
||||
and (
|
||||
self.acc_info.quotas.storage_size_gb
|
||||
!= aparam_quotas_storage_size_gb
|
||||
)
|
||||
):
|
||||
sdk_param_storage_size_quota_gb = aparam_quotas_storage_size_gb
|
||||
need_account_update_api_call = True
|
||||
|
||||
aparam_quotas_gpu_count: int | None = aparam_quotas['gpu_count']
|
||||
if (
|
||||
aparam_quotas_gpu_count is not None
|
||||
and self.acc_info.quotas.gpu_count != aparam_quotas_gpu_count
|
||||
):
|
||||
sdk_param_gpu_count_quota = aparam_quotas_gpu_count
|
||||
need_account_update_api_call = True
|
||||
|
||||
aparam_quotas_ext_ip_count: int | None = aparam_quotas[
|
||||
'ext_ip_count'
|
||||
]
|
||||
if (
|
||||
aparam_quotas_ext_ip_count is not None
|
||||
and (
|
||||
self.acc_info.quotas.ext_ip_count
|
||||
!= aparam_quotas_ext_ip_count
|
||||
)
|
||||
):
|
||||
sdk_param_ext_ip_count_quota = aparam_quotas_ext_ip_count
|
||||
need_account_update_api_call = True
|
||||
|
||||
aparam_quotas_ram_size_mb: int | None = aparam_quotas[
|
||||
'ram_size_mb'
|
||||
]
|
||||
if (
|
||||
aparam_quotas_ram_size_mb is not None
|
||||
and (
|
||||
self.acc_info.quotas.ram_size_mb
|
||||
!= aparam_quotas_ram_size_mb
|
||||
)
|
||||
):
|
||||
sdk_param_ram_size_quota_mb = aparam_quotas_ram_size_mb
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_sep_pools = None
|
||||
aparam_sep_pools: list[dict[str, Any]] = self.aparams['sep_pools']
|
||||
if aparam_sep_pools is not None:
|
||||
sep_pools: set[str] = set()
|
||||
for sep in aparam_sep_pools:
|
||||
sep_pool_names: list[str] = sep['pool_names']
|
||||
for pool_name in sep_pool_names:
|
||||
sep_pools.add(f'{sep["sep_id"]}_{pool_name}')
|
||||
if set(self.acc_info.sep_pools) != sep_pools:
|
||||
sdk_param_sep_pools = list(sep_pools)
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_description = None
|
||||
aparam_desc: str | None = self.aparams['description']
|
||||
if (
|
||||
aparam_desc is not None
|
||||
and self.acc_info.description != aparam_desc
|
||||
):
|
||||
sdk_param_description = aparam_desc
|
||||
need_account_update_api_call = True
|
||||
|
||||
sdk_param_default_zone_id = None
|
||||
aparam_default_zone_id: int | None = self.aparams['default_zone_id']
|
||||
if (
|
||||
aparam_default_zone_id is not None
|
||||
and self.acc_info.default_zone_id != aparam_default_zone_id
|
||||
):
|
||||
sdk_param_default_zone_id = aparam_default_zone_id
|
||||
need_account_update_api_call = True
|
||||
|
||||
if need_account_update_api_call:
|
||||
OBJ = 'account'
|
||||
|
||||
self.sdk_checkmode(self.api.ca.account.update)(
|
||||
account_id=self.acc_info.id,
|
||||
cpu_count_quota=sdk_param_cpu_count_quota,
|
||||
gpu_count_quota=sdk_param_gpu_count_quota,
|
||||
name=sdk_param_name,
|
||||
ext_ip_count_quota=sdk_param_ext_ip_count_quota,
|
||||
ram_size_quota_mb=sdk_param_ram_size_quota_mb,
|
||||
send_access_emails=sdk_param_send_access_emails,
|
||||
storage_size_quota_gb=sdk_param_storage_size_quota_gb,
|
||||
sep_pools=sdk_param_sep_pools,
|
||||
description=sdk_param_description,
|
||||
default_zone_id=sdk_param_default_zone_id,
|
||||
)
|
||||
|
||||
if sdk_param_send_access_emails is not None:
|
||||
smth = 'sending access emails'
|
||||
if sdk_param_send_access_emails:
|
||||
self.message(
|
||||
self.MESSAGES.obj_smth_enabled(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
smth=smth,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.message(
|
||||
self.MESSAGES.obj_smth_disabled(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
smth=smth,
|
||||
)
|
||||
)
|
||||
if sdk_param_name is not None:
|
||||
self.message(
|
||||
self.MESSAGES.obj_renamed(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
new_name=sdk_param_name,
|
||||
)
|
||||
)
|
||||
quotas = {
|
||||
'CPU count quota': sdk_param_cpu_count_quota,
|
||||
'storage size quota GB': sdk_param_storage_size_quota_gb,
|
||||
'GPU count quota': sdk_param_gpu_count_quota,
|
||||
'ext IP count quota': sdk_param_ext_ip_count_quota,
|
||||
'RAM size quota MB': sdk_param_ram_size_quota_mb,
|
||||
}
|
||||
for q_name, q_value in quotas.items():
|
||||
if q_value is not None:
|
||||
self.message(
|
||||
self.MESSAGES.obj_smth_changed(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
smth=q_name,
|
||||
new_value=q_value
|
||||
)
|
||||
)
|
||||
|
||||
if sdk_param_default_zone_id is not None:
|
||||
self.message(
|
||||
self.MESSAGES.obj_smth_changed(
|
||||
obj=OBJ,
|
||||
id=self.acc_info.id,
|
||||
smth='default_zone_id',
|
||||
new_value=sdk_param_default_zone_id,
|
||||
)
|
||||
)
|
||||
|
||||
self.get_info()
|
||||
|
||||
def change_state(self):
|
||||
match self._acc_info:
|
||||
case None:
|
||||
self.message(self.MESSAGES.obj_not_found(obj=self.OBJ))
|
||||
match self.aparams:
|
||||
case {'state': 'absent' | 'absent_permanently'}:
|
||||
pass
|
||||
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
||||
self.exit(fail=True)
|
||||
case {'status': 'DESTROYED'}:
|
||||
match self.aparams:
|
||||
case {'state': 'absent' | 'absent_permanently'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj=self.OBJ,
|
||||
id=self.acc_id,
|
||||
permanently=True,
|
||||
already=True,
|
||||
)
|
||||
if self._acc_info is None:
|
||||
self.message(self.MESSAGES.obj_not_found(obj=self.OBJ))
|
||||
match self.aparams:
|
||||
case {'state': 'absent' | 'absent_permanently'}:
|
||||
pass
|
||||
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
||||
self.exit(fail=True)
|
||||
elif self._acc_info.status == sdk_types.AccountStatus.DESTROYED:
|
||||
match self.aparams:
|
||||
case {'state': 'absent' | 'absent_permanently'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj=self.OBJ,
|
||||
id=self.acc_info.id,
|
||||
permanently=True,
|
||||
already=True,
|
||||
)
|
||||
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_restored(obj=self.OBJ,
|
||||
id=self.acc_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': 'confirmed' | 'disabled' | 'present'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_restored(obj=self.OBJ,
|
||||
id=self.acc_info.id)
|
||||
)
|
||||
case {'state': 'absent_permanently'}:
|
||||
self.delete(permanently=True)
|
||||
case {'state': 'confirmed' | 'present'}:
|
||||
self.restore()
|
||||
case {'state': 'disabled'}:
|
||||
self.restore()
|
||||
self.disable()
|
||||
case {'status': 'CONFIRMED'}:
|
||||
match self.aparams:
|
||||
case {'state': 'absent'}:
|
||||
self.delete()
|
||||
case {'state': 'absent_permanently'}:
|
||||
self.delete(permanently=True)
|
||||
case {'state': 'confirmed' | 'present'}:
|
||||
pass
|
||||
case {'state': 'disabled'}:
|
||||
self.disable()
|
||||
case {'status': '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
|
||||
self.exit(fail=True)
|
||||
elif self._acc_info.status == sdk_types.AccountStatus.DELETED:
|
||||
match self.aparams:
|
||||
case {'state': 'absent'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj=self.OBJ,
|
||||
id=self.acc_info.id,
|
||||
permanently=False,
|
||||
already=True,
|
||||
)
|
||||
)
|
||||
case {'state': 'absent_permanently'}:
|
||||
self.delete(permanently=True)
|
||||
case {'state': 'confirmed' | 'present'}:
|
||||
self.restore()
|
||||
case {'state': 'disabled'}:
|
||||
self.restore()
|
||||
self.disable()
|
||||
elif self._acc_info.status == sdk_types.AccountStatus.CONFIRMED:
|
||||
match self.aparams:
|
||||
case {'state': 'absent'}:
|
||||
self.delete()
|
||||
case {'state': 'absent_permanently'}:
|
||||
self.delete(permanently=True)
|
||||
case {'state': 'confirmed' | 'present'}:
|
||||
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):
|
||||
self.account_delete(account_id=self.acc_id, permanently=permanently)
|
||||
self.account_delete(
|
||||
account_id=self.acc_info.id,
|
||||
permanently=permanently
|
||||
)
|
||||
self.get_info()
|
||||
|
||||
def disable(self):
|
||||
self.account_disable(account_id=self.acc_id)
|
||||
self.sdk_checkmode(self.api.ca.account.disable)(
|
||||
account_id=self.acc_info.id
|
||||
)
|
||||
self.get_info()
|
||||
|
||||
def enable(self):
|
||||
self.account_enable(account_id=self.acc_id)
|
||||
self.sdk_checkmode(self.api.ca.account.enable)(
|
||||
account_id=self.acc_info.id
|
||||
)
|
||||
self.get_info()
|
||||
|
||||
def restore(self):
|
||||
self.account_restore(account_id=self.acc_id)
|
||||
self.account_restore(account_id=self.acc_info.id)
|
||||
self.get_info()
|
||||
|
||||
def change_acl(self):
|
||||
@@ -299,7 +502,7 @@ class DecortAccount(DecortController):
|
||||
return
|
||||
|
||||
actual_users = {
|
||||
u['userGroupId']: u['right'] for u in self.acc_info['acl']
|
||||
u.user_name: u.access_type for u in self.acc_info.acl
|
||||
}
|
||||
actual_users_ids = set(actual_users.keys())
|
||||
|
||||
@@ -328,8 +531,8 @@ class DecortAccount(DecortController):
|
||||
aparams_users_ids.intersection(actual_users_ids)
|
||||
upd_users = dict()
|
||||
for id in upd_users_ids:
|
||||
if actual_users[id] == 'CXDRAU':
|
||||
actual_user_rights = 'ARCXDU'
|
||||
if actual_users[id] == sdk_types.AccessType.CXDRAU:
|
||||
actual_user_rights = sdk_types.AccessTypeForSet.ARCXDU
|
||||
else:
|
||||
actual_user_rights = actual_users[id]
|
||||
|
||||
@@ -341,68 +544,12 @@ class DecortAccount(DecortController):
|
||||
actual_users_ids.difference(aparams_users_ids)
|
||||
|
||||
if del_users_ids or new_users or upd_users:
|
||||
self.account_change_acl(account_id=self.acc_id,
|
||||
self.account_change_acl(account_id=self.acc_info.id,
|
||||
del_users=del_users_ids,
|
||||
add_users=new_users,
|
||||
upd_users=upd_users)
|
||||
self.get_info()
|
||||
|
||||
@property
|
||||
def account_update_args(self) -> dict:
|
||||
result_args = dict()
|
||||
|
||||
aparam_access_emails = self.aparams['access_emails']
|
||||
if (aparam_access_emails is not None
|
||||
and self.acc_info['sendAccessEmails'] != aparam_access_emails):
|
||||
result_args['access_emails'] = aparam_access_emails
|
||||
|
||||
aparam_name = self.aparams['name']
|
||||
if (self.aparams['id'] and aparam_name
|
||||
and self.acc_info['name'] != aparam_name):
|
||||
result_args['name'] = aparam_name
|
||||
|
||||
aparam_quotas = self.aparams['quotas']
|
||||
if aparam_quotas:
|
||||
quotas_naming = [
|
||||
['cpu', 'CU_C', 'cpu_quota'],
|
||||
['disks_size', 'CU_DM', 'disks_size_quota'],
|
||||
['gpu', 'gpu_units', 'gpu_quota'],
|
||||
['public_ip', 'CU_I', 'public_ip_quota'],
|
||||
['ram', 'CU_M', 'ram_quota'],
|
||||
]
|
||||
for aparam, info_key, result_arg in quotas_naming:
|
||||
current_value = int(self.acc_info['resourceLimits'][info_key])
|
||||
if (aparam_quotas[aparam] is not None
|
||||
and current_value != aparam_quotas[aparam]):
|
||||
result_args[result_arg] = aparam_quotas[aparam]
|
||||
|
||||
aparam_sep_pools = self.aparams['sep_pools']
|
||||
if aparam_sep_pools is not None:
|
||||
sep_pools = set()
|
||||
for sep in aparam_sep_pools:
|
||||
for pool_name in sep['pool_names']:
|
||||
sep_pools.add(
|
||||
f'{sep["sep_id"]}_{pool_name}'
|
||||
)
|
||||
if set(self.acc_info['uniqPools']) != sep_pools:
|
||||
result_args['sep_pools'] = sep_pools
|
||||
|
||||
aparam_desc = self.aparams['description']
|
||||
if (
|
||||
aparam_desc is not None
|
||||
and self.acc_info['description'] != aparam_desc
|
||||
):
|
||||
result_args['description'] = aparam_desc
|
||||
|
||||
aparam_default_zone_id = self.aparams['default_zone_id']
|
||||
if (
|
||||
aparam_default_zone_id is not None
|
||||
and self.acc_info['defaultZoneId'] != aparam_default_zone_id
|
||||
):
|
||||
result_args['default_zone_id'] = aparam_default_zone_id
|
||||
|
||||
return result_args
|
||||
|
||||
def check_aparam_default_zone_id(self) -> bool | None:
|
||||
aparam_default_zone_id = self.aparams['default_zone_id']
|
||||
if aparam_default_zone_id is not None:
|
||||
|
||||
@@ -56,7 +56,7 @@ class DecortAccountList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -90,7 +90,7 @@ class DecortAuditList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -19,13 +19,12 @@ class decort_bservice(DecortController):
|
||||
|
||||
validated_acc_id = 0
|
||||
validated_rg_id = 0
|
||||
self.bservice_info = None
|
||||
self.is_bservice_stopped_or_will_be_stopped: None | bool = None
|
||||
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] == 0:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
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['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'],
|
||||
@@ -36,33 +35,46 @@ class decort_bservice(DecortController):
|
||||
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'],
|
||||
arg_amodule.params['account_name'])
|
||||
self.fail_json(**self.result)
|
||||
self.amodule.fail_json(**self.result)
|
||||
# fail the module -> exit
|
||||
# now validate RG
|
||||
validated_rg_id, validated_rg_facts = self.rg_find(
|
||||
validated_rg_id, validated_rg_model = self.rg_find(
|
||||
arg_account_id=validated_acc_id,
|
||||
arg_rg_id=arg_amodule.params['rg_id'],
|
||||
arg_rg_name=arg_amodule.params['rg_name']
|
||||
)
|
||||
if not validated_rg_id:
|
||||
if not validated_rg_id or not validated_rg_model:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||
arg_amodule.params['rg_name'])
|
||||
self.fail_json(**self.result)
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
arg_amodule.params['rg_id'] = validated_rg_id
|
||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
||||
validated_acc_id = validated_rg_facts['accountId']
|
||||
arg_amodule.params['rg_name'] = validated_rg_model.name
|
||||
validated_acc_id = validated_rg_model.account_id
|
||||
|
||||
self.bservice_id, self.bservice_info = self.bservice_find(
|
||||
validated_acc_id,
|
||||
validated_rg_id,
|
||||
arg_amodule.params['name'],
|
||||
arg_amodule.params['id']
|
||||
self.bservice_id, self._bservice_info = self.bservice_find(
|
||||
account_id=validated_acc_id,
|
||||
rg_id=validated_rg_id,
|
||||
bservice_name=arg_amodule.params['name'],
|
||||
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':
|
||||
self.bservice_should_exist = True
|
||||
@@ -92,8 +104,8 @@ class decort_bservice(DecortController):
|
||||
self.result['changed'] = False
|
||||
if self.bservice_id:
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for B-service ID {} in the "
|
||||
"current status '{}'.").format(self.bservice_id,
|
||||
self.amodule.params['state'],
|
||||
"current status '{}'.").format(self.amodule.params['state'],
|
||||
self.bservice_id,
|
||||
self.bservice_info['status'])
|
||||
else:
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for non-existent B-service name '{}' "
|
||||
@@ -104,15 +116,15 @@ class decort_bservice(DecortController):
|
||||
return
|
||||
|
||||
def create(self):
|
||||
self.bservice_id = self.bservice_id = self.bservice_provision(
|
||||
self.amodule.params['name'],
|
||||
self.amodule.params['rg_id'],
|
||||
self.amodule.params['sshuser'],
|
||||
self.amodule.params['sshkey'],
|
||||
self.bservice_id = self.sdk_checkmode(self.api.ca.bservice.create)(
|
||||
name=self.amodule.params['name'],
|
||||
rg_id=self.amodule.params['rg_id'],
|
||||
ssh_user_name=self.amodule.params['sshuser'],
|
||||
ssh_public_key=self.amodule.params['sshkey'],
|
||||
zone_id=self.aparams['zone_id'],
|
||||
)
|
||||
if self.bservice_id:
|
||||
_, 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_should_exist = True
|
||||
@@ -122,9 +134,9 @@ class decort_bservice(DecortController):
|
||||
self.bservice_state(self.bservice_info,d_state)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.bservice_info['zoneId']:
|
||||
self.bservice_migrate_to_zone(
|
||||
bs_id=self.bservice_id,
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.bservice_info['zone_id']:
|
||||
self.sdk_checkmode(self.api.cloudapi.bservice.migrate_to_zone)(
|
||||
bservice_id=self.bservice_id,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
return
|
||||
@@ -136,7 +148,10 @@ class decort_bservice(DecortController):
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
self.bservice_delete(self.bservice_id)
|
||||
self.sdk_checkmode(self.api.ca.bservice.delete)(
|
||||
bservice_id=self.bservice_id,
|
||||
permanently=True,
|
||||
)
|
||||
self.bservice_info['status'] = 'DELETED'
|
||||
self.bservice_should_exist = False
|
||||
return
|
||||
@@ -152,18 +167,9 @@ class decort_bservice(DecortController):
|
||||
)
|
||||
|
||||
if check_mode:
|
||||
# in check mode return immediately with the default values
|
||||
return ret_dict
|
||||
|
||||
ret_dict['id'] = self.bservice_info['id']
|
||||
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
|
||||
return self.bservice_info
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
@@ -225,14 +231,14 @@ class decort_bservice(DecortController):
|
||||
|
||||
self.is_bservice_stopped_or_will_be_stopped = (
|
||||
(
|
||||
self.bservice_info['techStatus'] == 'STOPPED'
|
||||
self.bservice_info['tech_status'] == 'STOPPED'
|
||||
and (
|
||||
self.aparams['state'] is None
|
||||
or self.aparams['state'] in ('present', 'stopped')
|
||||
)
|
||||
)
|
||||
or (
|
||||
self.bservice_info['techStatus'] != 'STOPPED'
|
||||
self.bservice_info['tech_status'] != 'STOPPED'
|
||||
and self.aparams['state'] == 'stopped'
|
||||
)
|
||||
)
|
||||
@@ -241,7 +247,7 @@ class decort_bservice(DecortController):
|
||||
check_errors = True
|
||||
if (
|
||||
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
|
||||
):
|
||||
check_errors = True
|
||||
@@ -267,7 +273,10 @@ class decort_bservice(DecortController):
|
||||
|
||||
if self.amodule.check_mode:
|
||||
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['facts'] = self.package_facts(amodule.check_mode)
|
||||
amodule.exit_json(**self.result)
|
||||
@@ -306,10 +315,12 @@ class decort_bservice(DecortController):
|
||||
else:
|
||||
self.action(amodule.params['state'])
|
||||
elif self.bservice_info['status'] == "DESTROYED":
|
||||
if amodule.params['state'] in ('present','enabled'):
|
||||
if amodule.params['state'] == 'present':
|
||||
self.create()
|
||||
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()
|
||||
else:
|
||||
state = amodule.params['state']
|
||||
@@ -326,7 +337,9 @@ class decort_bservice(DecortController):
|
||||
amodule.fail_json(**self.result)
|
||||
else:
|
||||
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)
|
||||
amodule.exit_json(**self.result)
|
||||
else:
|
||||
|
||||
@@ -58,27 +58,40 @@ class decort_disk(DecortController):
|
||||
name=arg_amodule.params['name'] if "name" in arg_amodule.params else "",
|
||||
account_id=self.acc_id,
|
||||
check_state=False,
|
||||
fail_if_not_found=False,
|
||||
)
|
||||
|
||||
if arg_amodule.params['place_with']:
|
||||
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_info = validated_disk_facts
|
||||
|
||||
if self.disk_id:
|
||||
self.acc_id = validated_disk_facts['accountId']
|
||||
if self.disk_id and self.disk_info.status not in [
|
||||
sdk_types.DiskStatus.DESTROYED,
|
||||
sdk_types.DiskStatus.PURGED,
|
||||
]:
|
||||
self.acc_id = validated_disk_facts.account_id
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
elif (
|
||||
self.amodule.params['state'] == 'present'
|
||||
and not arg_amodule.params['id']
|
||||
):
|
||||
self.check_amodule_args_for_create()
|
||||
|
||||
def compare_iotune_params(self, new_iotune: dict, current_iotune: dict):
|
||||
def compare_iotune_params(
|
||||
self,
|
||||
new_iotune: dict,
|
||||
current_iotune: sdk_types.IOTuneAPIResultNM,
|
||||
):
|
||||
io_fields = sdk_types.IOTuneAPIResultNM.model_fields.keys()
|
||||
|
||||
for field in io_fields:
|
||||
new_value = new_iotune.get(field)
|
||||
current_value = current_iotune.get(field)
|
||||
if new_value is None:
|
||||
continue
|
||||
current_value = getattr(current_iotune, field, None)
|
||||
|
||||
if new_value != current_value:
|
||||
return False
|
||||
@@ -115,7 +128,11 @@ class decort_disk(DecortController):
|
||||
)
|
||||
#IO tune
|
||||
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
|
||||
if self.amodule.params['shareable']:
|
||||
self.sdk_checkmode(self.api.cloudapi.disks.share)(
|
||||
@@ -133,13 +150,13 @@ class decort_disk(DecortController):
|
||||
#rename if id present
|
||||
if (
|
||||
self.amodule.params['name'] is not None
|
||||
and self.amodule.params['name'] != self.disk_info['name']
|
||||
and self.amodule.params['name'] != self.disk_info.name
|
||||
):
|
||||
self.rename()
|
||||
#resize
|
||||
if (
|
||||
self.amodule.params['size'] is not None
|
||||
and self.amodule.params['size'] != self.disk_info['sizeMax']
|
||||
and self.amodule.params['size'] != self.disk_info.size_max_gb
|
||||
):
|
||||
self.sdk_checkmode(self.api.cloudapi.disks.resize2)(
|
||||
disk_id=self.disk_id,
|
||||
@@ -147,16 +164,19 @@ class decort_disk(DecortController):
|
||||
)
|
||||
#IO TUNE
|
||||
aparam_limit_io: dict[str, int | None] = self.amodule.params['limitIO']
|
||||
if aparam_limit_io:
|
||||
if (
|
||||
aparam_limit_io
|
||||
and any(value is not None for value in aparam_limit_io.values())
|
||||
):
|
||||
if not self.compare_iotune_params(
|
||||
new_iotune=aparam_limit_io,
|
||||
current_iotune=self.disk_info['iotune'],
|
||||
current_iotune=self.disk_info.io_tune,
|
||||
):
|
||||
self.limit_io(aparam_limit_io=aparam_limit_io)
|
||||
|
||||
#share check/update
|
||||
#raise Exception(self.amodule.params['shareable'])
|
||||
if self.amodule.params['shareable'] != self.disk_info['shareable']:
|
||||
if self.amodule.params['shareable'] != self.disk_info.shared:
|
||||
if self.amodule.params['shareable']:
|
||||
self.sdk_checkmode(self.api.cloudapi.disks.share)(
|
||||
disk_id=self.disk_id,
|
||||
@@ -169,7 +189,7 @@ class decort_disk(DecortController):
|
||||
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
||||
if (
|
||||
aparam_storage_policy_id is not None
|
||||
and aparam_storage_policy_id != self.disk_info['storage_policy_id']
|
||||
and aparam_storage_policy_id != self.disk_info.storage_policy_id
|
||||
):
|
||||
self.sdk_checkmode(self.api.ca.disks.change_disk_storage_policy)(
|
||||
disk_id=self.disk_id,
|
||||
@@ -183,7 +203,7 @@ class decort_disk(DecortController):
|
||||
detach=self.amodule.params['force_detach'],
|
||||
permanently=self.amodule.params['permanently'],
|
||||
)
|
||||
self.disk_id, self.disk_info = self._disk_get_by_id(self.disk_id)
|
||||
self.disk_info = self._disk_get_by_id(disk_id=self.disk_id)
|
||||
return
|
||||
|
||||
def rename(self):
|
||||
@@ -199,7 +219,7 @@ class decort_disk(DecortController):
|
||||
self.result['changed'] = False
|
||||
if self.disk_id:
|
||||
self.result['msg'] = ("No state change required for Disk ID {} because of its "
|
||||
"current status '{}'.").format(self.disk_id, self.disk_info['status'])
|
||||
"current status '{}'.").format(self.disk_id, self.disk_info.status)
|
||||
else:
|
||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
||||
"non-existent Disk.").format(self.amodule.params['state'])
|
||||
@@ -219,28 +239,7 @@ class decort_disk(DecortController):
|
||||
if check_mode or self.disk_info is None:
|
||||
return ret_dict
|
||||
|
||||
# remove io param with zero value
|
||||
clean_io = [param for param in self.disk_info['iotune'] if self.disk_info['iotune'][param] == 0]
|
||||
for key in clean_io: del self.disk_info['iotune'][key]
|
||||
|
||||
ret_dict['id'] = self.disk_info['id']
|
||||
ret_dict['name'] = self.disk_info['name']
|
||||
ret_dict['size'] = self.disk_info['sizeMax']
|
||||
ret_dict['state'] = self.disk_info['status']
|
||||
ret_dict['account_id'] = self.disk_info['accountId']
|
||||
ret_dict['sep_id'] = self.disk_info['sepId']
|
||||
ret_dict['pool'] = self.disk_info['pool']
|
||||
ret_dict['computes'] = self.disk_info['computes']
|
||||
ret_dict['gid'] = self.disk_info['gid']
|
||||
ret_dict['iotune'] = self.disk_info['iotune']
|
||||
ret_dict['size_available'] = self.disk_info['sizeAvailable']
|
||||
ret_dict['size_used'] = self.disk_info['sizeUsed']
|
||||
ret_dict['storage_policy_id'] = self.disk_info['storage_policy_id']
|
||||
ret_dict['to_clean'] = self.disk_info['to_clean']
|
||||
ret_dict['cache_mode'] = self.disk_info['cache']
|
||||
ret_dict['blkdiscard'] = self.disk_info['blkdiscard']
|
||||
|
||||
return ret_dict
|
||||
return self.disk_info.model_dump()
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
@@ -391,8 +390,8 @@ class decort_disk(DecortController):
|
||||
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
||||
if (
|
||||
aparam_storage_policy_id is not None
|
||||
and aparam_storage_policy_id
|
||||
not in self.acc_info['storage_policy_ids']
|
||||
and aparam_storage_policy_id
|
||||
not in self.acc_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -431,24 +430,24 @@ class decort_disk(DecortController):
|
||||
amodule = self.amodule
|
||||
if self.disk_id:
|
||||
#disk exist
|
||||
if self.disk_info['status'] in ["MODELED", "CREATING"]:
|
||||
if self.disk_info.status in [sdk_types.DiskStatus.MODELED, sdk_types.DiskStatus.CREATING]:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("No change can be done for existing Disk ID {} because of its current "
|
||||
"status '{}'").format(self.disk_id, self.disk_info['status'])
|
||||
"status '{}'").format(self.disk_id, self.disk_info.status)
|
||||
# "ASSIGNED","CREATED","DELETED","PURGED", "DESTROYED"
|
||||
elif self.disk_info['status'] in ["ASSIGNED","CREATED"]:
|
||||
elif self.disk_info.status in [sdk_types.DiskStatus.CREATED, sdk_types.DiskStatus.ASSIGNED]:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.delete()
|
||||
elif amodule.params['state'] == 'present':
|
||||
self.action()
|
||||
elif self.disk_info['status'] in ["PURGED", "DESTROYED"]:
|
||||
elif self.disk_info.status in [sdk_types.DiskStatus.PURGED, sdk_types.DiskStatus.DESTROYED]:
|
||||
#re-provision disk
|
||||
if amodule.params['state'] in ('present'):
|
||||
self.create()
|
||||
else:
|
||||
self.nop()
|
||||
elif self.disk_info['status'] == "DELETED":
|
||||
elif self.disk_info.status == sdk_types.DiskStatus.DELETED:
|
||||
if amodule.params['state'] in ('present'):
|
||||
self.action(restore=True)
|
||||
elif (amodule.params['state'] == 'absent' and
|
||||
@@ -459,8 +458,24 @@ class decort_disk(DecortController):
|
||||
else:
|
||||
# preexisting Disk was not found
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.nop()
|
||||
else:
|
||||
self.exit()
|
||||
|
||||
if (
|
||||
(
|
||||
amodule.params['state'] == 'present'
|
||||
or amodule.params['state'] is None
|
||||
)
|
||||
and amodule.params['id']
|
||||
):
|
||||
self.message(
|
||||
f'Disk with ID {amodule.params['id']} not found.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
if (
|
||||
amodule.params['state'] == 'present'
|
||||
and not amodule.params['id']
|
||||
):
|
||||
self.create()
|
||||
|
||||
if self.result['failed']:
|
||||
|
||||
@@ -59,9 +59,11 @@ class DecortDiskList(DecortController):
|
||||
storage_policy_id=dict(
|
||||
type='int',
|
||||
),
|
||||
type=dict(
|
||||
type='str',
|
||||
choices=sdk_types.DiskType._member_names_,
|
||||
rg_id=dict(
|
||||
type='int',
|
||||
),
|
||||
vm_id=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -75,7 +77,7 @@ class DecortDiskList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -108,7 +110,6 @@ class DecortDiskList(DecortController):
|
||||
def get_info(self):
|
||||
aparam_filter: dict[str, Any] = self.aparams['filter']
|
||||
aparam_status: str | None = aparam_filter['status']
|
||||
aparam_type: str | None = aparam_filter['type']
|
||||
|
||||
aparam_pagination: dict[str, Any] = self.aparams['pagination']
|
||||
|
||||
@@ -137,10 +138,8 @@ class DecortDiskList(DecortController):
|
||||
if aparam_status else None
|
||||
),
|
||||
storage_policy_id=aparam_filter['storage_policy_id'],
|
||||
type=(
|
||||
sdk_types.DiskType[aparam_type]
|
||||
if aparam_type else None
|
||||
),
|
||||
rg_id=aparam_filter['rg_id'],
|
||||
vm_id=aparam_filter['vm_id'],
|
||||
page_number=aparam_pagination['number'],
|
||||
page_size=aparam_pagination['size'],
|
||||
sort_by=sort_by,
|
||||
|
||||
@@ -75,7 +75,7 @@ class DecortFlipGroupList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -21,7 +21,12 @@ class decort_group(DecortController):
|
||||
validated_bservice_id = None
|
||||
#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:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
@@ -29,15 +34,14 @@ class decort_group(DecortController):
|
||||
self.amodule.fail_json(**self.result)
|
||||
#find group
|
||||
self.bservice_id = validated_bservice_id
|
||||
self.bservice_info = bservice_info
|
||||
self.group_id,self.group_info = self.group_find(
|
||||
bs_id=validated_bservice_id,
|
||||
bs_info=bservice_info,
|
||||
bs_info=self.bservice_info,
|
||||
group_id=arg_amodule.params['id'],
|
||||
group_name=arg_amodule.params['name'],
|
||||
)
|
||||
self.acc_id = self.bservice_info['accountId']
|
||||
self.rg_id = self.bservice_info['rgId']
|
||||
self.acc_id = self.bservice_info['account_id']
|
||||
self.rg_id = self.bservice_info['rg_id']
|
||||
|
||||
if self.group_id:
|
||||
self.group_should_exist = True
|
||||
@@ -88,17 +92,19 @@ class decort_group(DecortController):
|
||||
warning=True,
|
||||
)
|
||||
|
||||
self.group_id=self.group_provision(
|
||||
bs_id=self.bservice_id,
|
||||
arg_name=self.amodule.params['name'],
|
||||
arg_count=self.amodule.params['count'],
|
||||
arg_cpu=self.amodule.params['cpu'],
|
||||
arg_ram=self.amodule.params['ram'],
|
||||
arg_boot_disk=self.amodule.params['boot_disk'],
|
||||
arg_image_id=self.amodule.params['image_id'],
|
||||
arg_role=self.amodule.params['role'],
|
||||
arg_network=self.amodule.params['networks'],
|
||||
arg_timeout=self.amodule.params['timeoutStart'],
|
||||
aparam_networks = self.amodule.params['networks'] or []
|
||||
self.group_id = self.sdk_checkmode(self.api.cloudapi.bservice.group_add)(
|
||||
bservice_id=self.bservice_id,
|
||||
name=self.amodule.params['name'],
|
||||
vm_count=self.amodule.params['count'],
|
||||
cpu_count=self.amodule.params['cpu'],
|
||||
ram_size_mb=self.amodule.params['ram'],
|
||||
boot_disk_size_gb=self.amodule.params['boot_disk'],
|
||||
image_id=self.amodule.params['image_id'],
|
||||
role=self.amodule.params['role'],
|
||||
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,
|
||||
storage_policy_id=self.aparams['storage_policy_id'],
|
||||
)
|
||||
@@ -146,14 +152,15 @@ class decort_group(DecortController):
|
||||
aparam_value = self.aparams[aparam_name]
|
||||
group_info_value = self.group_info[info_key]
|
||||
if aparam_value != None and aparam_value != group_info_value:
|
||||
self.group_update(
|
||||
bs_id=self.bservice_id,
|
||||
gr_dict=self.group_info,
|
||||
arg_cpu=self.aparams['cpu'],
|
||||
arg_disk=self.aparams['boot_disk'],
|
||||
arg_name=self.aparams['name'],
|
||||
arg_role=self.aparams['role'],
|
||||
arg_ram=self.aparams['ram'],
|
||||
self.sdk_checkmode(self.api.cloudapi.bservice.group_update)(
|
||||
bservice_id=self.bservice_id,
|
||||
bsgroup_id=self.group_info['id'],
|
||||
force=True,
|
||||
cpu_count=self.aparams['cpu'],
|
||||
boot_disk_size_gb=self.aparams['boot_disk'],
|
||||
name=self.aparams['name'],
|
||||
ram_size_mb=self.aparams['ram'],
|
||||
role=self.aparams['role'],
|
||||
)
|
||||
break
|
||||
|
||||
@@ -166,9 +173,9 @@ class decort_group(DecortController):
|
||||
|
||||
def destroy(self):
|
||||
|
||||
self.group_delete(
|
||||
self.bservice_id,
|
||||
self.group_id
|
||||
self.sdk_checkmode(self.api.cloudapi.bservice.group_remove)(
|
||||
bservice_id=self.bservice_id,
|
||||
bsgroup_id=self.group_id,
|
||||
)
|
||||
self.group_should_exist = False
|
||||
|
||||
@@ -356,7 +363,7 @@ class decort_group(DecortController):
|
||||
else:
|
||||
if (
|
||||
aparam_storage_policy_id
|
||||
not in self.rg_info['storage_policy_ids']
|
||||
not in self.rg_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -366,7 +373,7 @@ class decort_group(DecortController):
|
||||
)
|
||||
if (
|
||||
aparam_storage_policy_id
|
||||
not in self.acc_info['storage_policy_ids']
|
||||
not in self.acc_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
|
||||
@@ -18,8 +18,8 @@ class decort_image(DecortController):
|
||||
super(decort_image, self).__init__(AnsibleModule(**self.amodule_init_args))
|
||||
amodule = self.amodule
|
||||
|
||||
self.validated_image_id = 0
|
||||
self.validated_virt_image_id = 0
|
||||
self.validated_image_id: int = 0
|
||||
self.validated_virt_image_id: int = 0
|
||||
self.validated_image_name = amodule.params['image_name']
|
||||
self.validated_virt_image_name = None
|
||||
self.image_info: dict
|
||||
@@ -46,9 +46,9 @@ class decort_image(DecortController):
|
||||
)
|
||||
if self.virt_image_info:
|
||||
_, 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 (
|
||||
self.aparams['virt_name'] is not None
|
||||
and self.aparams['virt_name']
|
||||
@@ -61,10 +61,10 @@ class decort_image(DecortController):
|
||||
or self.aparams['image_name'] is not None
|
||||
):
|
||||
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:
|
||||
self.acc_id = self.image_info['accountId']
|
||||
self.acc_id = self.image_info['account_id']
|
||||
if (
|
||||
amodule.params['image_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)
|
||||
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:
|
||||
self.check_amodule_args_for_change()
|
||||
elif self.validated_virt_image_id:
|
||||
@@ -81,12 +98,13 @@ class decort_image(DecortController):
|
||||
else:
|
||||
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
|
||||
image_id, image_facts = self.image_find(image_id=amodule.params['image_id'], image_name=self.validated_image_name,
|
||||
account_id=self.validated_account_id, rg_id=0,
|
||||
sepid=amodule.params['sep_id'],
|
||||
pool=amodule.params['pool'])
|
||||
pool=amodule.params['pool'],
|
||||
fail_if_not_found=fail_if_not_found,)
|
||||
return image_id, image_facts
|
||||
|
||||
def decort_virt_image_find(self, amodule):
|
||||
@@ -150,28 +168,30 @@ class decort_image(DecortController):
|
||||
)
|
||||
|
||||
# function that creates OS image
|
||||
image_facts = self.image_create(
|
||||
img_name=self.validated_image_name,
|
||||
url=amodule.params['url'],
|
||||
self.sdk_checkmode(self.api.ca.image.create)(
|
||||
name=self.validated_image_name,
|
||||
download_url=amodule.params['url'],
|
||||
boot_mode=boot_mode,
|
||||
boot_loader_type=loader_type,
|
||||
hot_resize=hot_resize,
|
||||
username=amodule.params['image_username'],
|
||||
password=amodule.params['image_password'],
|
||||
os_user_name=amodule.params['image_username'],
|
||||
os_password=amodule.params['image_password'],
|
||||
account_id=self.validated_account_id,
|
||||
usernameDL=amodule.params['usernameDL'],
|
||||
passwordDL=amodule.params['passwordDL'],
|
||||
sepId=amodule.params['sepId'],
|
||||
poolName=amodule.params['poolName'],
|
||||
download_user_name=amodule.params['usernameDL'],
|
||||
download_password=amodule.params['passwordDL'],
|
||||
sep_id=amodule.params['sepId'],
|
||||
sep_pool_name=amodule.params['poolName'],
|
||||
network_interface_naming=network_interface_naming,
|
||||
storage_policy_id=amodule.params['storage_policy_id'],
|
||||
)
|
||||
self.result['changed'] = True
|
||||
return image_facts
|
||||
return
|
||||
|
||||
def decort_virt_image_link(self,amodule):
|
||||
# function that links an OS image to a virtual one
|
||||
self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.target_image_id)
|
||||
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)
|
||||
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,
|
||||
@@ -180,32 +200,41 @@ class decort_image(DecortController):
|
||||
|
||||
def decort_image_delete(self,amodule):
|
||||
# function that removes an image
|
||||
self.image_delete(imageId=amodule.image_id_delete)
|
||||
self.sdk_checkmode(self.api.ca.image.delete)(
|
||||
image_id=amodule.image_id_delete
|
||||
)
|
||||
_, image_facts = decort_image._image_get_by_id(self, amodule.image_id_delete)
|
||||
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
||||
return
|
||||
|
||||
def decort_virt_image_create(self,amodule):
|
||||
# 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'],
|
||||
target_id=self.target_image_id,
|
||||
target_image_id=self.target_image_id,
|
||||
account_id=self.aparams['account_id'],
|
||||
)
|
||||
image_id, image_facts = decort_image.decort_virt_image_find(self, amodule)
|
||||
self.result['facts'] = decort_image.decort_image_package_facts(image_facts, amodule.check_mode)
|
||||
return image_id, image_facts
|
||||
|
||||
|
||||
@DecortController.handle_sdk_exceptions
|
||||
def decort_image_rename(self,amodule):
|
||||
# image renaming function
|
||||
image_facts = self.image_rename(imageId=self.validated_image_id, name=amodule.params['image_name'])
|
||||
self.sdk_checkmode(self.api.ca.image.rename)(
|
||||
image_id=self.validated_image_id,
|
||||
name=amodule.params['image_name'],
|
||||
)
|
||||
self.result['msg'] = ("Image renamed successfully")
|
||||
image_id, image_facts = decort_image.decort_image_find(self, amodule)
|
||||
return image_id, image_facts
|
||||
|
||||
|
||||
@DecortController.handle_sdk_exceptions
|
||||
def decort_virt_image_rename(self, amodule):
|
||||
image_facts = self.image_rename(imageId=self.validated_virt_image_id,
|
||||
name=amodule.params['virt_name'])
|
||||
self.sdk_checkmode(self.api.ca.image.rename)(
|
||||
image_id=self.validated_virt_image_id,
|
||||
name=amodule.params['virt_name'],
|
||||
)
|
||||
self.result['msg'] = ("Virtual image renamed successfully")
|
||||
image_id, image_facts = self.decort_virt_image_find(amodule)
|
||||
return image_id, image_facts
|
||||
@@ -241,14 +270,14 @@ class decort_image(DecortController):
|
||||
|
||||
ret_dict['id'] = arg_image_facts['id']
|
||||
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['sep_id'] = arg_image_facts['sepId']
|
||||
ret_dict['pool'] = arg_image_facts['pool']
|
||||
ret_dict['sep_id'] = arg_image_facts['sep_id']
|
||||
ret_dict['pool'] = arg_image_facts['sep_pool_name']
|
||||
ret_dict['state'] = arg_image_facts['status']
|
||||
ret_dict['linkto'] = arg_image_facts['linkTo']
|
||||
ret_dict['accountId'] = arg_image_facts['accountId']
|
||||
ret_dict['boot_mode'] = arg_image_facts['bootType']
|
||||
ret_dict['linkto'] = arg_image_facts['link_to']
|
||||
ret_dict['accountId'] = arg_image_facts['account_id']
|
||||
ret_dict['boot_mode'] = arg_image_facts['boot_mode']
|
||||
|
||||
ret_dict['boot_loader_type'] = ''
|
||||
match arg_image_facts['type']:
|
||||
@@ -259,11 +288,13 @@ class decort_image(DecortController):
|
||||
ret_dict['boot_loader_type'] = boot_loader_type
|
||||
|
||||
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['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
|
||||
|
||||
@property
|
||||
@@ -372,7 +403,7 @@ class decort_image(DecortController):
|
||||
if (
|
||||
aparam_storage_policy_id is not None
|
||||
and aparam_storage_policy_id
|
||||
not in self.acc_info['storage_policy_ids']
|
||||
not in self.acc_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -426,7 +457,7 @@ class decort_image(DecortController):
|
||||
)
|
||||
elif (
|
||||
aparam_storage_policy_id
|
||||
not in self.acc_info['storage_policy_ids']
|
||||
not in self.acc_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -491,7 +522,7 @@ class decort_image(DecortController):
|
||||
and amodule.params['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,
|
||||
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:
|
||||
amodule.image_id_delete = self.validated_virt_image_id
|
||||
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)
|
||||
|
||||
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:
|
||||
amodule.image_id_delete = self.validated_image_id
|
||||
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)
|
||||
|
||||
if self.validated_image_id:
|
||||
@@ -528,7 +562,7 @@ class decort_image(DecortController):
|
||||
and amodule.params['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,
|
||||
storage_policy_id=amodule.params['storage_policy_id'],
|
||||
)
|
||||
|
||||
@@ -62,7 +62,8 @@ class DecortImageList(DecortController):
|
||||
choices=sdk_types.ImageStatus._member_names_,
|
||||
),
|
||||
type=dict(
|
||||
type='str',
|
||||
type='list',
|
||||
elements='str',
|
||||
choices=sdk_types.ImageType._member_names_,
|
||||
),
|
||||
storage_policy_id=dict(
|
||||
@@ -145,7 +146,7 @@ class DecortImageList(DecortController):
|
||||
),
|
||||
storage_policy_id=aparam_filter['storage_policy_id'],
|
||||
type=(
|
||||
sdk_types.ImageType[aparam_type]
|
||||
[sdk_types.ImageType[type_] for type_ in aparam_type]
|
||||
if aparam_type else None
|
||||
),
|
||||
page_number=aparam_pagination['number'],
|
||||
|
||||
@@ -20,7 +20,7 @@ class decort_k8s(DecortController):
|
||||
|
||||
validated_acc_id = 0
|
||||
validated_rg_id = 0
|
||||
validated_rg_facts = None
|
||||
validated_rg_model = None
|
||||
validated_k8ci_id = 0
|
||||
self.k8s_should_exist = False
|
||||
self.is_k8s_stopped_or_will_be_stopped: None | bool = None
|
||||
@@ -55,12 +55,12 @@ class decort_k8s(DecortController):
|
||||
self.amodule.fail_json(**self.result)
|
||||
# fail the module -> exit
|
||||
# now validate RG
|
||||
validated_rg_id, validated_rg_facts = self.rg_find(
|
||||
validated_rg_id, validated_rg_model = self.rg_find(
|
||||
arg_account_id=validated_acc_id,
|
||||
arg_rg_id=arg_amodule.params['rg_id'],
|
||||
arg_rg_name=arg_amodule.params['rg_name']
|
||||
)
|
||||
if not validated_rg_id:
|
||||
if not validated_rg_id or not validated_rg_model:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||
@@ -70,19 +70,21 @@ class decort_k8s(DecortController):
|
||||
|
||||
self.rg_id = validated_rg_id
|
||||
arg_amodule.params['rg_id'] = validated_rg_id
|
||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
||||
self.acc_id = validated_rg_facts['accountId']
|
||||
arg_amodule.params['rg_name'] = validated_rg_model.name
|
||||
self.acc_id = validated_rg_model.account_id
|
||||
|
||||
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=arg_amodule.params['id'],
|
||||
k8s_name=arg_amodule.params['name'],
|
||||
rg_id=validated_rg_id,
|
||||
check_state=False)
|
||||
self.k8s_id, self._k8s_info = self.k8s_find(
|
||||
k8s_id=arg_amodule.params['id'],
|
||||
k8s_name=arg_amodule.params['name'],
|
||||
rg_id=validated_rg_id,
|
||||
check_state=False,
|
||||
)
|
||||
|
||||
if self.k8s_id and self.k8s_info['status'] != 'DESTROYED':
|
||||
self.k8s_should_exist = True
|
||||
self.acc_id = self.k8s_info['accountId']
|
||||
self.acc_id = self.k8s_info['account_id']
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
elif arg_amodule.params['state'] != 'absent':
|
||||
self.check_amodule_args_for_create()
|
||||
return
|
||||
|
||||
@@ -96,32 +98,16 @@ class decort_k8s(DecortController):
|
||||
config=None,
|
||||
)
|
||||
|
||||
if self.amodule.params['getConfig'] and self.k8s_info['techStatus'] == "STARTED":
|
||||
ret_dict['config'] = self.k8s_getConfig()
|
||||
|
||||
if check_mode:
|
||||
# in check mode return immediately with the default values
|
||||
return ret_dict
|
||||
|
||||
#if self.k8s_facts is None:
|
||||
# #if void facts provided - change state value to ABSENT and return
|
||||
# ret_dict['state'] = "ABSENT"
|
||||
# return ret_dict
|
||||
|
||||
ret_dict['id'] = self.k8s_info['id']
|
||||
ret_dict['name'] = self.k8s_info['name']
|
||||
ret_dict['techStatus'] = self.k8s_info['techStatus']
|
||||
ret_dict['state'] = self.k8s_info['status']
|
||||
ret_dict['rg_id'] = self.k8s_info['rgId']
|
||||
ret_dict['vins_id'] = self.k8s_vins_id
|
||||
ret_dict['account_id'] = self.acc_id
|
||||
ret_dict['k8s_Masters'] = self.k8s_info['k8sGroups']['masters']
|
||||
ret_dict['k8s_Workers'] = self.k8s_info['k8sGroups']['workers']
|
||||
ret_dict['lb_id'] = self.k8s_info['lbId']
|
||||
ret_dict['description'] = self.k8s_info['desc']
|
||||
ret_dict['zone_id'] = self.k8s_info['zoneId']
|
||||
|
||||
return ret_dict
|
||||
self.k8s_info['vins_id'] = self.k8s_vins_id
|
||||
self.k8s_info['config'] = None
|
||||
if self.amodule.params['getConfig'] and self.k8s_info['tech_status'] == "STARTED":
|
||||
self.k8s_info['config'] = self.api.cloudapi.k8s.get_config(
|
||||
k8s_id=self.k8s_id,
|
||||
)
|
||||
return self.k8s_info
|
||||
|
||||
def nop(self):
|
||||
"""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.amodule.fail_json(**self.result)
|
||||
|
||||
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=k8s_id,
|
||||
k8s_name=self.amodule.params['name'],
|
||||
rg_id=self.rg_id,
|
||||
check_state=False)
|
||||
self.k8s_id, self._k8s_info = self.k8s_find(
|
||||
k8s_id=k8s_id,
|
||||
k8s_name=self.amodule.params['name'],
|
||||
rg_id=self.rg_id,
|
||||
check_state=False,
|
||||
)
|
||||
|
||||
if self.k8s_id:
|
||||
self.k8s_should_exist = True
|
||||
@@ -215,21 +203,26 @@ class decort_k8s(DecortController):
|
||||
self.k8s_workers_modify(
|
||||
arg_k8swg=self.k8s_info,
|
||||
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
|
||||
|
||||
def destroy(self):
|
||||
self.k8s_delete(self.k8s_id,self.amodule.params['permanent'])
|
||||
self.sdk_checkmode(self.api.ca.k8s.delete)(
|
||||
k8s_id=self.k8s_id,
|
||||
permanently=self.amodule.params['permanent'],
|
||||
)
|
||||
self.k8s_info['status'] = 'DELETED'
|
||||
self.k8s_should_exist = False
|
||||
return
|
||||
|
||||
def action(self, disared_state, preupdate: bool = False):
|
||||
if self.amodule.params['master_chipset'] is not None:
|
||||
for master_node in self.k8s_info['k8sGroups']['masters'][
|
||||
'detailedInfo'
|
||||
for master_node in self.k8s_info['node_groups']['master'][
|
||||
'vms'
|
||||
]:
|
||||
_, master_node_info, _ = self._compute_get_by_id(
|
||||
comp_id=master_node['id']
|
||||
@@ -245,17 +238,27 @@ class decort_k8s(DecortController):
|
||||
self.exit(fail=True)
|
||||
|
||||
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:
|
||||
# K8s info updating
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
#k8s state
|
||||
self.k8s_state(self.k8s_info, disared_state)
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
#check groups and modify if needed
|
||||
if self.aparams['workers'] is not None:
|
||||
self.k8s_workers_modify(
|
||||
@@ -264,14 +267,17 @@ class decort_k8s(DecortController):
|
||||
)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.k8s_info['zoneId']:
|
||||
self.k8s_migrate_to_zone(
|
||||
if (
|
||||
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,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
if self.result['changed'] == True:
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
#TODO check workers metadata and modify if needed
|
||||
|
||||
return
|
||||
@@ -411,7 +417,6 @@ class decort_k8s(DecortController):
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
default='Created by decort ansible module',
|
||||
),
|
||||
with_lb=dict(
|
||||
type='bool',
|
||||
@@ -471,25 +476,25 @@ class decort_k8s(DecortController):
|
||||
|
||||
self.is_k8s_stopped_or_will_be_stopped = (
|
||||
(
|
||||
self.k8s_info['techStatus'] == 'STOPPED'
|
||||
self.k8s_info['tech_status'] == 'STOPPED'
|
||||
and (
|
||||
self.aparams['state'] is None
|
||||
or self.aparams['state'] in ('present', 'stopped')
|
||||
)
|
||||
)
|
||||
or (
|
||||
self.k8s_info['techStatus'] != 'STOPPED'
|
||||
self.k8s_info['tech_status'] != 'STOPPED'
|
||||
and self.aparams['state'] == 'stopped'
|
||||
)
|
||||
)
|
||||
|
||||
aparam_sysctl = self.aparams['lb_sysctl']
|
||||
if aparam_sysctl is not None:
|
||||
_, lb_info = self._lb_get_by_id(lb_id=self.k8s_info['lbId'])
|
||||
lb_model = self._lb_get_by_id(lb_id=self.k8s_info['lb_id'])
|
||||
sysctl_with_str_values = {
|
||||
k: str(v) for k, v in aparam_sysctl.items()
|
||||
}
|
||||
if sysctl_with_str_values != lb_info['sysctlParams']:
|
||||
if sysctl_with_str_values != lb_model.sysctl_params:
|
||||
self.message(
|
||||
'Check for parameter "lb_sysctl" failed: '
|
||||
'cannot change lb_sysctl for an existing cluster '
|
||||
@@ -501,7 +506,7 @@ class decort_k8s(DecortController):
|
||||
check_errors = True
|
||||
if (
|
||||
self.aparams['zone_id'] is not None
|
||||
and self.aparams['zone_id'] != self.k8s_info['zoneId']
|
||||
and self.aparams['zone_id'] != self.k8s_info['zone_id']
|
||||
and not self.is_k8s_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
@@ -513,13 +518,11 @@ class decort_k8s(DecortController):
|
||||
aparam_storage_policy_id = self.aparams['storage_policy_id']
|
||||
if aparam_storage_policy_id is not None:
|
||||
computes_ids = []
|
||||
for master_node in self.k8s_info['k8sGroups']['masters'][
|
||||
'detailedInfo'
|
||||
]:
|
||||
for master_node in self.k8s_info['node_groups']['master']['vms']:
|
||||
computes_ids.append(master_node['id'])
|
||||
for wg in self.k8s_info['k8sGroups']['workers']:
|
||||
for wg in self.k8s_info['node_groups']['worker']:
|
||||
workers_ids = [
|
||||
worker['id'] for worker in wg['detailedInfo']
|
||||
worker['id'] for worker in wg['vms']
|
||||
]
|
||||
computes_ids.extend(workers_ids)
|
||||
for compute_id in computes_ids:
|
||||
@@ -587,7 +590,7 @@ class decort_k8s(DecortController):
|
||||
)
|
||||
elif (
|
||||
aparam_storage_policy_id
|
||||
not in self.rg_info['storage_policy_ids']
|
||||
not in self.rg_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -628,7 +631,9 @@ class decort_k8s(DecortController):
|
||||
if amodule.params['state'] in (
|
||||
'disabled', 'enabled', 'present', 'started', 'stopped'
|
||||
):
|
||||
self.k8s_restore(self.k8s_id)
|
||||
self.sdk_checkmode(self.api.ca.k8s.restore)(
|
||||
k8s_id=self.k8s_id,
|
||||
)
|
||||
self.action(disared_state=amodule.params['state'],
|
||||
preupdate=True)
|
||||
if amodule.params['state'] == 'absent':
|
||||
|
||||
@@ -18,11 +18,9 @@ class decort_lb(DecortController):
|
||||
arg_amodule = self.amodule
|
||||
|
||||
self.lb_id = 0
|
||||
self.lb_facts = None
|
||||
self.vins_id = 0
|
||||
self.vins_facts = None
|
||||
self.rg_id = 0
|
||||
self.rg_facts = None
|
||||
self.rg_model = None
|
||||
self.default_server_check = "enabled"
|
||||
self.default_alg = "roundrobin"
|
||||
self.default_settings = {
|
||||
@@ -38,22 +36,34 @@ class decort_lb(DecortController):
|
||||
self.is_lb_stopped_or_will_be_stopped: None | bool = None
|
||||
|
||||
if arg_amodule.params['lb_id']:
|
||||
self.lb_id, self.lb_facts = self.lb_find(arg_amodule.params['lb_id'])
|
||||
if not self.lb_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Specified LB ID {} not found."\
|
||||
.format(arg_amodule.params['lb_id'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.rg_id = self.lb_facts['rgId']
|
||||
self.vins_id = self.lb_facts['vinsId']
|
||||
_, self._lb_info = self.lb_find(lb_id=arg_amodule.params['lb_id'])
|
||||
if self._lb_info is None:
|
||||
if arg_amodule.params['state'] == 'absent':
|
||||
self.nop()
|
||||
self.exit()
|
||||
else:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='lb',
|
||||
id=arg_amodule.params['lb_id'],
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
self.lb_id = self._lb_info.id
|
||||
self.rg_id = self._lb_info.rg_id
|
||||
self.vins_id = self._lb_info.vins_id
|
||||
|
||||
elif arg_amodule.params['rg_id']:
|
||||
self.rg_id, self.rg_facts = self.rg_find(0,arg_amodule.params['rg_id'], arg_rg_name="")
|
||||
if not self.rg_id:
|
||||
self.rg_id, self.rg_model = self.rg_find(
|
||||
0,
|
||||
arg_amodule.params['rg_id'],
|
||||
arg_rg_name="",
|
||||
)
|
||||
if not self.rg_id or not self.rg_model:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Specified RG ID {} not found.".format(arg_amodule.params['rg_id'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.acc_id = self.rg_facts['accountId']
|
||||
self.acc_id = self.rg_model.account_id
|
||||
|
||||
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
|
||||
if not arg_amodule.params['rg_name']:
|
||||
@@ -67,24 +77,22 @@ class decort_lb(DecortController):
|
||||
self.result['msg'] = ("Current user does not have access to the requested account "
|
||||
"or non-existent account specified.")
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.rg_id, self.rg_facts = self.rg_find(self.acc_id,0, arg_rg_name=arg_amodule.params['rg_name'])
|
||||
self.rg_id, self.rg_model = self.rg_find(
|
||||
self.acc_id,
|
||||
0,
|
||||
arg_rg_name=arg_amodule.params['rg_name'],
|
||||
)
|
||||
|
||||
if arg_amodule.params['vins_id']:
|
||||
self.vins_id, self.vins_facts = self.vins_find(
|
||||
self.vins_id = self._vins_get_by_id(
|
||||
vins_id=arg_amodule.params['vins_id']
|
||||
)
|
||||
if not self.vins_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = (
|
||||
f'Specified ViNS ID {arg_amodule.params["vins_id"]}'
|
||||
f' not found'
|
||||
)
|
||||
self.amodule.fail_json(**self.result)
|
||||
).id
|
||||
elif arg_amodule.params['vins_name']:
|
||||
self.vins_id, self.vins_facts = self.vins_find(
|
||||
self.vins_id, _ = self.vins_find(
|
||||
vins_id=arg_amodule.params['vins_id'],
|
||||
vins_name=arg_amodule.params['vins_name'],
|
||||
rg_id=self.rg_id)
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
if not self.vins_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = (
|
||||
@@ -94,10 +102,17 @@ class decort_lb(DecortController):
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
if self.rg_id and arg_amodule.params['lb_name']:
|
||||
self.lb_id, self.lb_facts = self.lb_find(0,arg_amodule.params['lb_name'],self.rg_id)
|
||||
self.lb_id, self._lb_info = self.lb_find(
|
||||
0,
|
||||
arg_amodule.params['lb_name'],
|
||||
self.rg_id,
|
||||
)
|
||||
|
||||
if self.lb_id and self.lb_facts['status'] != 'DESTROYED':
|
||||
self.acc_id = self.lb_facts['accountId']
|
||||
if (
|
||||
self._lb_info
|
||||
and self._lb_info.status != sdk_types.LBStatus.DESTROYED
|
||||
):
|
||||
self.acc_id = self._lb_info.account_id
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
@@ -115,54 +130,73 @@ class decort_lb(DecortController):
|
||||
zone_id=self.aparams['zone_id'],
|
||||
start=start_after_create,
|
||||
)
|
||||
if self.lb_id and (self.amodule.params['backends'] or
|
||||
self.amodule.params['frontends']):
|
||||
self.lb_id, self.lb_facts = self.lb_find(0,self.amodule.params['lb_name'],self.rg_id)
|
||||
if (
|
||||
self.lb_id and (
|
||||
self.amodule.params['backends']
|
||||
or self.amodule.params['frontends']
|
||||
)
|
||||
):
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.lb_update(
|
||||
lb_facts=self.lb_facts,
|
||||
lb_model=self.lb_info,
|
||||
aparam_backends=self.amodule.params['backends'],
|
||||
aparam_frontends=self.amodule.params['frontends'],
|
||||
aparam_servers=self.amodule.params['servers'],
|
||||
)
|
||||
return
|
||||
|
||||
def action(self,d_state='',restore=False):
|
||||
if restore == True:
|
||||
self.lb_restore(lb_id=self.lb_id)
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.lb_state(self.lb_facts, 'enabled')
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
|
||||
def action(
|
||||
self,
|
||||
d_state='',
|
||||
restore=False,
|
||||
):
|
||||
if restore:
|
||||
self.sdk_checkmode(self.api.ca.lb.restore)(
|
||||
lb_id=self.lb_id,
|
||||
)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.lb_state(self.lb_info, 'enabled')
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
self.lb_update(
|
||||
lb_facts=self.lb_facts,
|
||||
lb_model=self.lb_info,
|
||||
aparam_backends=self.amodule.params['backends'],
|
||||
aparam_frontends=self.amodule.params['frontends'],
|
||||
aparam_servers=self.amodule.params['servers'],
|
||||
aparam_sysctl=self.aparams['sysctl'],
|
||||
)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
if d_state != '':
|
||||
self.lb_state(self.lb_facts, d_state)
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.lb_state(self.lb_info, d_state)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
if (d_state == 'enabled' and
|
||||
self.lb_facts.get('status') == 'ENABLED' and
|
||||
self.lb_facts.get('techStatus') == 'STOPPED'):
|
||||
self.lb_state(self.lb_facts, 'started')
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
if (
|
||||
d_state == 'enabled'
|
||||
and self.lb_info.status == sdk_types.LBStatus.ENABLED
|
||||
and self.lb_info.tech_status == sdk_types.LBTechStatus.STOPPED
|
||||
):
|
||||
self.lb_state(self.lb_info, 'started')
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.lb_facts['zoneId']:
|
||||
self.lb_migrate_to_zone(
|
||||
lb_id=self.lb_id,
|
||||
if (
|
||||
aparam_zone_id is not None
|
||||
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,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
def delete(self):
|
||||
self.lb_delete(self.lb_id, self.amodule.params['permanently'])
|
||||
self.lb_id, self.lb_facts = self._lb_get_by_id(self.lb_id)
|
||||
self.sdk_checkmode(self.api.ca.lb.delete)(
|
||||
lb_id=self.lb_id,
|
||||
permanently=self.amodule.params['permanently'],
|
||||
)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
return
|
||||
|
||||
def nop(self):
|
||||
@@ -173,30 +207,39 @@ class decort_lb(DecortController):
|
||||
"""
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = False
|
||||
if self.lb_id:
|
||||
self.result['msg'] = ("No state change required for LB ID {} because of its "
|
||||
"current status '{}'.").format(self.lb_id, self.lb_facts['status'])
|
||||
if self._lb_info:
|
||||
self.result['msg'] = (
|
||||
f'No state change required for LB ID {self._lb_info.id} '
|
||||
f'because of its current status "{self._lb_info.status}".'
|
||||
)
|
||||
else:
|
||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
||||
"non-existent LB instance.").format(self.amodule.params['state'])
|
||||
self.result['msg'] = (
|
||||
f'No state change to "{self.amodule.params['state']}" '
|
||||
f'can be done for non-existent LB instance.'
|
||||
)
|
||||
return
|
||||
|
||||
def error(self):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
if self.vins_id:
|
||||
if self._lb_info:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for LB ID {} in the "
|
||||
"current status '{}'").format(self.lb_id,
|
||||
self.amodule.params['state'],
|
||||
self.lb_facts['status'])
|
||||
self.result['msg'] = (
|
||||
f'Invalid target state "{self.amodule.params['state']}" '
|
||||
f'requested for LB ID {self._lb_info.id} in the current status '
|
||||
f'"{self._lb_info.status}".'
|
||||
)
|
||||
else:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for non-existent "
|
||||
"LB name '{}'").format(self.amodule.params['state'],
|
||||
self.amodule.params['lb_name'])
|
||||
self.result['msg'] = (
|
||||
f'Invalid target state "{self.amodule.params['state']}" '
|
||||
f'requested for non-existent LB name '
|
||||
f'"{self.amodule.params['lb_name']}".'
|
||||
)
|
||||
return
|
||||
|
||||
def package_facts(self, arg_check_mode=False):
|
||||
"""Package a dictionary of LB facts according to the decort_lb module specification.
|
||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||
@@ -205,34 +248,16 @@ class decort_lb(DecortController):
|
||||
@param arg_check_mode: boolean that tells if this Ansible module is run in check mode
|
||||
"""
|
||||
|
||||
ret_dict = dict(id=0,
|
||||
name="none",
|
||||
state="CHECK_MODE",
|
||||
sysctl={},
|
||||
)
|
||||
ret_dict = {}
|
||||
|
||||
if arg_check_mode:
|
||||
# in check mode return immediately with the default values
|
||||
return ret_dict
|
||||
|
||||
if self.lb_facts is None:
|
||||
# if void facts provided - change state value to ABSENT and return
|
||||
ret_dict['state'] = "ABSENT"
|
||||
if self._lb_info is None:
|
||||
return ret_dict
|
||||
|
||||
ret_dict['id'] = self.lb_facts['id']
|
||||
ret_dict['name'] = self.lb_facts['name']
|
||||
ret_dict['state'] = self.lb_facts['status']
|
||||
ret_dict['account_id'] = self.lb_facts['accountId']
|
||||
ret_dict['rg_id'] = self.lb_facts['rgId']
|
||||
ret_dict['gid'] = self.lb_facts['gid']
|
||||
if self.amodule.params['state']!="absent":
|
||||
ret_dict['backends'] = self.lb_facts['backends']
|
||||
ret_dict['frontends'] = self.lb_facts['frontends']
|
||||
ret_dict['sysctl'] = self.lb_facts['sysctlParams']
|
||||
ret_dict['zone_id'] = self.lb_facts['zoneId']
|
||||
ret_dict['tech_status'] = self.lb_facts['techStatus']
|
||||
return ret_dict
|
||||
return self._lb_info.model_dump()
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
@@ -321,18 +346,16 @@ class decort_lb(DecortController):
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
|
||||
lb_info: dict = self.lb_facts
|
||||
self.is_lb_stopped_or_will_be_stopped = (
|
||||
self.is_lb_stopped_or_will_be_stopped = (
|
||||
(
|
||||
lb_info['techStatus'] == 'STOPPED'
|
||||
self.lb_info.tech_status == sdk_types.LBTechStatus.STOPPED
|
||||
and (
|
||||
self.aparams['state'] is None
|
||||
or self.aparams['state'] in ('present', 'stopped')
|
||||
)
|
||||
)
|
||||
or (
|
||||
lb_info['techStatus'] != 'STOPPED'
|
||||
self.lb_info.tech_status != sdk_types.LBTechStatus.STOPPED
|
||||
and self.aparams['state'] == 'stopped'
|
||||
)
|
||||
)
|
||||
@@ -341,7 +364,7 @@ class decort_lb(DecortController):
|
||||
check_errors = True
|
||||
if (
|
||||
self.aparams['zone_id'] is not None
|
||||
and self.aparams['zone_id'] != lb_info['zoneId']
|
||||
and self.aparams['zone_id'] != self.lb_info.zone_id
|
||||
and not self.is_lb_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
@@ -364,18 +387,32 @@ class decort_lb(DecortController):
|
||||
@DecortController.handle_sdk_exceptions
|
||||
def run(self):
|
||||
amodule = self.amodule
|
||||
if self.lb_id:
|
||||
if self.lb_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING","DESTROYING","RESTORING"]:
|
||||
if self._lb_info:
|
||||
if self._lb_info.status in [
|
||||
sdk_types.LBStatus.MODELED,
|
||||
sdk_types.LBStatus.DISABLING,
|
||||
sdk_types.LBStatus.ENABLING,
|
||||
sdk_types.LBStatus.DELETING,
|
||||
sdk_types.LBStatus.DESTROYING,
|
||||
sdk_types.LBStatus.RESTORING,
|
||||
]:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("No change can be done for existing LB ID {} because of its current "
|
||||
"status '{}'").format(self.lb_id, self.lb_facts['status'])
|
||||
elif self.lb_facts['status'] in ('DISABLED', 'ENABLED', 'CREATED'):
|
||||
self.result['msg'] = (
|
||||
f'No change can be done for existing LB ID '
|
||||
f'{self._lb_info.id} because of its current status '
|
||||
f'"{self._lb_info.status.name}".'
|
||||
)
|
||||
elif self._lb_info.status in [
|
||||
sdk_types.LBStatus.DISABLED,
|
||||
sdk_types.LBStatus.ENABLED,
|
||||
sdk_types.LBStatus.CREATED,
|
||||
]:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.delete()
|
||||
else:
|
||||
self.action(d_state=amodule.params['state'])
|
||||
elif self.lb_facts['status'] == "DELETED":
|
||||
elif self._lb_info.status == sdk_types.LBStatus.DELETED:
|
||||
if amodule.params['state'] == 'present':
|
||||
self.action(restore=True)
|
||||
elif amodule.params['state'] == 'enabled':
|
||||
@@ -385,7 +422,7 @@ class decort_lb(DecortController):
|
||||
self.delete()
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
self.error()
|
||||
elif self.lb_facts['status'] == "DESTROYED":
|
||||
elif self._lb_info.status == sdk_types.LBStatus.DESTROYED:
|
||||
if amodule.params['state'] in ('present', 'enabled'):
|
||||
self.create()
|
||||
elif amodule.params['state'] == 'absent':
|
||||
@@ -407,7 +444,7 @@ class decort_lb(DecortController):
|
||||
amodule.fail_json(**self.result)
|
||||
else:
|
||||
if self.result['changed']:
|
||||
_, self.lb_facts = self.lb_find(lb_id=self.lb_id)
|
||||
self._lb_info = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
self.result['facts'] = self.package_facts(amodule.check_mode)
|
||||
amodule.exit_json(**self.result)
|
||||
|
||||
|
||||
@@ -43,7 +43,13 @@ class decort_pfw(DecortController):
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def decort_pfw_package_facts(self, comp_facts, vins_facts, pfw_facts, check_mode=False):
|
||||
def decort_pfw_package_facts(
|
||||
self,
|
||||
comp_facts,
|
||||
vins_model: sdk_types.CloudapiVinsGetResultModel,
|
||||
pfw_facts,
|
||||
check_mode=False,
|
||||
):
|
||||
"""Package a dictionary of PFW rules facts according to the decort_pfw module specification.
|
||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||
the module run.
|
||||
@@ -68,9 +74,13 @@ class decort_pfw(DecortController):
|
||||
ret_dict['state'] = "ABSENT"
|
||||
return ret_dict
|
||||
|
||||
gw_vnf = vins_model.vnfs.gw
|
||||
ret_dict['compute_id'] = comp_facts['id']
|
||||
ret_dict['vins_id'] = vins_facts['id']
|
||||
ret_dict['public_ip'] = vins_facts['vnfs']['GW']['config']['ext_net_ip']
|
||||
ret_dict['vins_id'] = vins_model.id
|
||||
if gw_vnf:
|
||||
ret_dict['public_ip'] = gw_vnf.config.ext_net_ip
|
||||
else:
|
||||
raise RuntimeError('VINS GW VNF must exist.')
|
||||
|
||||
if len(pfw_facts) != 0:
|
||||
ret_dict['state'] = 'PRESENT'
|
||||
@@ -106,16 +116,15 @@ class decort_pfw(DecortController):
|
||||
self.result['msg'] = "Cannot find specified Compute ID {}.".format(amodule.params['compute_id'])
|
||||
amodule.fail_json(**self.result)
|
||||
|
||||
validated_vins_id, vins_facts = self.vins_find(amodule.params['vins_id'])
|
||||
if not validated_vins_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Cannot find specified ViNS ID {}.".format(amodule.params['vins_id'])
|
||||
amodule.fail_json(**self.result)
|
||||
vins_model = self._vins_get_by_id(vins_id=amodule.params['vins_id'])
|
||||
|
||||
gw_vnf_facts = vins_facts['vnfs'].get('GW')
|
||||
if not gw_vnf_facts or gw_vnf_facts['status'] == "DESTROYED":
|
||||
gw_vnf = vins_model.vnfs.gw
|
||||
if not gw_vnf or gw_vnf.status == sdk_types.VNFStatus.DESTROYED:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "ViNS ID {} does not have a configured external connection.".format(validated_vins_id)
|
||||
self.result['msg'] = (
|
||||
f'ViNS ID {vins_model.id} does not '
|
||||
f'have a configured external connection.'
|
||||
)
|
||||
amodule.fail_json(**self.result)
|
||||
|
||||
#
|
||||
@@ -124,12 +133,20 @@ class decort_pfw(DecortController):
|
||||
|
||||
if amodule.params['state'] == 'absent':
|
||||
# ignore amodule.params['rules'] and remove all rules associated with this Compute
|
||||
pfw_facts = self.pfw_configure(comp_facts, vins_facts, None)
|
||||
pfw_facts = self.pfw_configure(
|
||||
comp_facts,
|
||||
vins_model,
|
||||
None,
|
||||
)
|
||||
elif amodule.params['rules'] is not None:
|
||||
# manage PFW rules accodring to the module arguments
|
||||
pfw_facts = self.pfw_configure(comp_facts, vins_facts, amodule.params['rules'])
|
||||
pfw_facts = self.pfw_configure(
|
||||
comp_facts,
|
||||
vins_model,
|
||||
amodule.params['rules'],
|
||||
)
|
||||
else:
|
||||
pfw_facts = self._pfw_get(comp_facts['id'], vins_facts['id'])
|
||||
pfw_facts = self._pfw_get(comp_facts['id'], vins_model.id)
|
||||
|
||||
#
|
||||
# complete module run
|
||||
@@ -138,7 +155,12 @@ class decort_pfw(DecortController):
|
||||
amodule.fail_json(**self.result)
|
||||
else:
|
||||
# prepare PFW facts to be returned as part of self.result and then call exit_json(...)
|
||||
self.result['facts'] = self.decort_pfw_package_facts(comp_facts, vins_facts, pfw_facts, amodule.check_mode)
|
||||
self.result['facts'] = self.decort_pfw_package_facts(
|
||||
comp_facts,
|
||||
vins_model,
|
||||
pfw_facts,
|
||||
amodule.check_mode,
|
||||
)
|
||||
amodule.exit_json(**self.result)
|
||||
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ class decort_rg(DecortController):
|
||||
amodule = self.amodule
|
||||
|
||||
self.validated_acc_id = 0
|
||||
self.validated_rg_id = 0
|
||||
self.validated_rg_facts = None
|
||||
self.rg_id: int = 0
|
||||
|
||||
if amodule.params['rg_id'] is None:
|
||||
if self.amodule.params['account_id']:
|
||||
@@ -42,13 +41,15 @@ class decort_rg(DecortController):
|
||||
else:
|
||||
self.rg_should_exist = False
|
||||
|
||||
if self.validated_rg_id and self.rg_facts['status'] != 'DESTROYED':
|
||||
if self.rg_id and (
|
||||
self.rg_info.status != sdk_types.ResourceGroupStatus.DESTROYED
|
||||
):
|
||||
self.check_amodule_args_for_change()
|
||||
|
||||
def get_info(self):
|
||||
# If this is the first getting info
|
||||
if not self.validated_rg_id:
|
||||
self.validated_rg_id, self.rg_facts = self.rg_find(
|
||||
if self._rg_info is None:
|
||||
self.rg_id, self._rg_info = self.rg_find(
|
||||
arg_account_id=self.validated_acc_id,
|
||||
arg_rg_id=self.aparams['rg_id'],
|
||||
arg_rg_name=self.aparams['rg_name'],
|
||||
@@ -61,16 +62,16 @@ class decort_rg(DecortController):
|
||||
if self.amodule.check_mode:
|
||||
return
|
||||
|
||||
_, self.rg_facts = self.rg_find(arg_rg_id=self.validated_rg_id)
|
||||
_, self._rg_info = self.rg_find(arg_rg_id=self.rg_id)
|
||||
|
||||
def access(self):
|
||||
should_change_access = False
|
||||
acc_granted = False
|
||||
for rg_item in self.rg_facts['acl']:
|
||||
if rg_item['userGroupId'] == self.amodule.params['access']['user']:
|
||||
for rg_item in self.rg_info.acl:
|
||||
if rg_item.user_name == self.amodule.params['access']['user']:
|
||||
acc_granted = True
|
||||
if self.amodule.params['access']['action'] == 'grant':
|
||||
if rg_item['right'] != self.amodule.params['access']['right']:
|
||||
if rg_item.access_type.value != self.amodule.params['access']['right']:
|
||||
should_change_access = True
|
||||
if self.amodule.params['access']['action'] == 'revoke':
|
||||
should_change_access = True
|
||||
@@ -78,19 +79,30 @@ class decort_rg(DecortController):
|
||||
should_change_access = True
|
||||
|
||||
if should_change_access == True:
|
||||
self.rg_access(self.validated_rg_id, self.amodule.params['access'])
|
||||
self.rg_facts['access'] = self.amodule.params['access']
|
||||
if self.amodule.params['access']['action'] == "grant":
|
||||
self.sdk_checkmode(self.api.ca.rg.access_grant)(
|
||||
access_type=sdk_types.AccessTypeForSet(
|
||||
self.amodule.params['access']['right']
|
||||
),
|
||||
rg_id=self.rg_id,
|
||||
user_name=self.amodule.params['access']['user'],
|
||||
)
|
||||
else:
|
||||
self.sdk_checkmode(self.api.ca.rg.access_revoke)(
|
||||
rg_id=self.rg_id,
|
||||
user_name=self.amodule.params['access']['user'],
|
||||
)
|
||||
self.rg_should_exist = True
|
||||
return
|
||||
|
||||
def error(self):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
if self.validated_rg_id > 0:
|
||||
if self.rg_id:
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for rg ID {} in the "
|
||||
"current status '{}'.").format(self.validated_rg_id,
|
||||
"current status '{}'.").format(self.rg_id,
|
||||
self.amodule.params['state'],
|
||||
self.rg_facts['status'])
|
||||
self.rg_info.status.value)
|
||||
else:
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for non-existent rg name '{}' "
|
||||
"in account ID {} ").format(self.amodule.params['state'],
|
||||
@@ -99,20 +111,31 @@ class decort_rg(DecortController):
|
||||
return
|
||||
|
||||
def update(self):
|
||||
resources = self.rg_facts['Resources']['Reserved']
|
||||
try:
|
||||
rg_res_model = (
|
||||
self.api.cloudapi.rg.get_resource_consumption(rg_id=self.rg_id)
|
||||
)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
self.message(
|
||||
msg=(
|
||||
f'Failed to get RG Resources by ID {self.rg_id}: {e}'
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
reserved_resources = rg_res_model.reserved
|
||||
incorrect_quota = dict(Requested=dict(),
|
||||
Reserved=dict(),)
|
||||
query_key_map = dict(
|
||||
cpu='cpu',
|
||||
ram='ram',
|
||||
disk='disksize',
|
||||
ext_ips='extips',
|
||||
storage_policies='policies',
|
||||
cpu='cpu_count',
|
||||
ram='ram_size_mb',
|
||||
disk='storage_size_gb_by_real_usage',
|
||||
ext_ips='ext_ip_count',
|
||||
storage_policies='storage_policies',
|
||||
)
|
||||
if self.amodule.params['quotas']:
|
||||
for quota_item in self.amodule.params['quotas']:
|
||||
if quota_item == 'storage_policies':
|
||||
rg_storage_policies = resources[query_key_map[quota_item]]
|
||||
rg_storage_policies = reserved_resources.storage_policies
|
||||
aparam_storage_policies = self.amodule.params['quotas'][
|
||||
quota_item
|
||||
]
|
||||
@@ -124,32 +147,35 @@ class decort_rg(DecortController):
|
||||
if (
|
||||
rg_storage_policy
|
||||
and aparam_storage_policy['storage_size_gb']
|
||||
< rg_storage_policy['disksize']
|
||||
< rg_storage_policy.storage_size_gb_by_real_usage
|
||||
):
|
||||
incorrect_quota['Requested'][quota_item] = (
|
||||
self.amodule.params['quotas'][quota_item]
|
||||
)
|
||||
incorrect_quota['Reserved'][quota_item] = (
|
||||
resources[query_key_map[quota_item]]
|
||||
getattr(
|
||||
reserved_resources,
|
||||
query_key_map[quota_item]
|
||||
)
|
||||
)
|
||||
elif (
|
||||
self.amodule.params['quotas'][quota_item]
|
||||
< resources[query_key_map[quota_item]]
|
||||
< getattr(reserved_resources, query_key_map[quota_item])
|
||||
):
|
||||
incorrect_quota['Requested'][quota_item] = (
|
||||
self.amodule.params['quotas'][quota_item]
|
||||
)
|
||||
incorrect_quota['Reserved'][quota_item] = (
|
||||
resources[query_key_map[quota_item]]
|
||||
getattr(reserved_resources, query_key_map[quota_item])
|
||||
)
|
||||
|
||||
if incorrect_quota['Requested']:
|
||||
if reserved_resources and incorrect_quota['Requested']:
|
||||
self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota)
|
||||
self.result['failed'] = True
|
||||
|
||||
if not self.result['failed']:
|
||||
self.rg_update(
|
||||
arg_rg_dict=self.rg_facts,
|
||||
rg_model=self.rg_info,
|
||||
arg_quotas=self.amodule.params['quotas'],
|
||||
arg_res_types=self.amodule.params['resType'],
|
||||
arg_newname=self.amodule.params['rename'],
|
||||
@@ -160,13 +186,15 @@ class decort_rg(DecortController):
|
||||
return
|
||||
|
||||
def setDefNet(self):
|
||||
rg_def_net_type = self.rg_facts['def_net_type']
|
||||
rg_def_net_id = self.rg_facts['def_net_id']
|
||||
rg_def_net_type = self.rg_info.default_net_type.value
|
||||
rg_def_net_id = self.rg_info.default_net_id
|
||||
aparam_def_net_type = self.aparams['def_netType']
|
||||
aparam_def_net_id = self.aparams['def_netId']
|
||||
|
||||
need_to_reset = (aparam_def_net_type == 'NONE'
|
||||
and rg_def_net_type != aparam_def_net_type)
|
||||
need_to_reset = (
|
||||
aparam_def_net_type == sdk_types.RGDefaultNetType.NONE
|
||||
and rg_def_net_type != aparam_def_net_type
|
||||
)
|
||||
|
||||
need_to_change = False
|
||||
if aparam_def_net_id is not None:
|
||||
@@ -174,16 +202,26 @@ class decort_rg(DecortController):
|
||||
or aparam_def_net_type != rg_def_net_type)
|
||||
|
||||
if need_to_reset or need_to_change:
|
||||
self.rg_setDefNet(
|
||||
arg_rg_id=self.validated_rg_id,
|
||||
arg_net_type=aparam_def_net_type,
|
||||
arg_net_id=aparam_def_net_id,
|
||||
)
|
||||
if aparam_def_net_type == sdk_types.RGDefaultNetType.NONE:
|
||||
self.sdk_checkmode(self.api.ca.rg.remove_def_net)(
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
else:
|
||||
if aparam_def_net_type == sdk_types.RGDefaultNetType.PRIVATE:
|
||||
net_type = sdk_types.RGDefaultNetTypeForSet.PRIVATE
|
||||
elif aparam_def_net_type == sdk_types.RGDefaultNetType.PUBLIC:
|
||||
net_type = sdk_types.RGDefaultNetTypeForSet.PUBLIC
|
||||
|
||||
self.sdk_checkmode(self.api.ca.rg.set_def_net)(
|
||||
net_type=net_type,
|
||||
rg_id=self.rg_id,
|
||||
net_id=aparam_def_net_id,
|
||||
)
|
||||
self.rg_should_exist = True
|
||||
return
|
||||
|
||||
def create(self):
|
||||
self.validated_rg_id = self.rg_provision(
|
||||
self.rg_id = self.rg_provision(
|
||||
self.validated_acc_id,
|
||||
self.amodule.params['rg_name'],
|
||||
self.amodule.params['owner'],
|
||||
@@ -196,12 +234,13 @@ class decort_rg(DecortController):
|
||||
self.amodule.params['quotas'],
|
||||
"", # this is location code. TODO: add module argument
|
||||
sdn_access_group_id=self.aparams['sdn_access_group_id'],
|
||||
enable_default_gateway=self.aparams['enable_default_gateway'],
|
||||
)
|
||||
|
||||
if self.validated_rg_id:
|
||||
self.validated_rg_id, self.rg_facts = self.rg_find(
|
||||
if self.rg_id:
|
||||
self.rg_id, self._rg_info = self.rg_find(
|
||||
arg_account_id=self.validated_acc_id,
|
||||
arg_rg_id=self.validated_rg_id,
|
||||
arg_rg_id=self.rg_id,
|
||||
arg_rg_name="",
|
||||
arg_check_state=False
|
||||
)
|
||||
@@ -209,31 +248,30 @@ class decort_rg(DecortController):
|
||||
return
|
||||
|
||||
def enable(self):
|
||||
self.rg_enable(self.validated_rg_id,
|
||||
self.amodule.params['state'])
|
||||
if self.amodule.params['state'] == "enabled":
|
||||
self.rg_facts['status'] = 'CREATED'
|
||||
else:
|
||||
self.rg_facts['status'] = 'DISABLED'
|
||||
self.sdk_checkmode(self.api.ca.rg.enable)(
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
elif self.amodule.params['state'] == "disabled":
|
||||
self.sdk_checkmode(self.api.ca.rg.disable)(
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
self.rg_should_exist = True
|
||||
return
|
||||
|
||||
def restore(self):
|
||||
self.rg_restore(self.validated_rg_id)
|
||||
self.rg_facts['status'] = 'DISABLED'
|
||||
self.sdk_checkmode(self.api.ca.rg.restore)(
|
||||
rg_id=self.rg_id,
|
||||
)
|
||||
self.rg_should_exist = True
|
||||
return
|
||||
|
||||
def destroy(self):
|
||||
self.rg_delete(
|
||||
rg_id=self.validated_rg_id,
|
||||
self.sdk_checkmode(self.api.ca.rg.delete)(
|
||||
rg_id=self.rg_id,
|
||||
permanently=self.amodule.params['permanently'],
|
||||
recursively=self.aparams['recursive_deletion'],
|
||||
)
|
||||
if self.amodule.params['permanently'] == True:
|
||||
self.rg_facts['status'] = 'DESTROYED'
|
||||
else:
|
||||
self.rg_facts['status'] = 'DELETED'
|
||||
self.rg_should_exist = False
|
||||
return
|
||||
|
||||
@@ -259,23 +297,7 @@ class decort_rg(DecortController):
|
||||
# ret_dict['state'] = "ABSENT"
|
||||
# return ret_dict
|
||||
|
||||
ret_dict['id'] = self.rg_facts['id']
|
||||
ret_dict['name'] = self.rg_facts['name']
|
||||
ret_dict['state'] = self.rg_facts['status']
|
||||
ret_dict['account_id'] = self.rg_facts['accountId']
|
||||
ret_dict['gid'] = self.rg_facts['gid']
|
||||
ret_dict['quota'] = self.rg_facts['resourceLimits']
|
||||
ret_dict['resTypes'] = self.rg_facts['resourceTypes']
|
||||
ret_dict['defNetId'] = self.rg_facts['def_net_id']
|
||||
ret_dict['defNetType'] = self.rg_facts['def_net_type']
|
||||
ret_dict['ViNS'] = self.rg_facts['vins']
|
||||
ret_dict['computes'] = self.rg_facts['vms']
|
||||
ret_dict['uniqPools'] = self.rg_facts['uniqPools']
|
||||
ret_dict['description'] = self.rg_facts['desc']
|
||||
ret_dict['sdn_access_group_id'] = self.rg_facts['sdn_access_group_id']
|
||||
ret_dict['storage_policy_ids'] = self.rg_facts['storage_policy_ids']
|
||||
|
||||
return ret_dict
|
||||
return self.rg_info.model_dump()
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
@@ -290,6 +312,24 @@ class decort_rg(DecortController):
|
||||
),
|
||||
access=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
action=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
choices=[
|
||||
'grant',
|
||||
'revoke',
|
||||
],
|
||||
),
|
||||
user=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
right=dict(
|
||||
type='str',
|
||||
choices=sdk_types.AccessTypeForSet._member_names_,
|
||||
),
|
||||
),
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
@@ -297,12 +337,8 @@ class decort_rg(DecortController):
|
||||
),
|
||||
def_netType=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'PRIVATE',
|
||||
'PUBLIC',
|
||||
'NONE',
|
||||
],
|
||||
default='PRIVATE',
|
||||
choices=sdk_types.RGDefaultNetType._member_names_,
|
||||
default=sdk_types.RGDefaultNetType.PRIVATE.name,
|
||||
),
|
||||
def_netId=dict(
|
||||
type='int',
|
||||
@@ -375,6 +411,9 @@ class decort_rg(DecortController):
|
||||
sdn_access_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
enable_default_gateway=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
@@ -386,13 +425,73 @@ class decort_rg(DecortController):
|
||||
self.aparams['sdn_access_group_id'] is not None
|
||||
and (
|
||||
self.aparams['sdn_access_group_id']
|
||||
!= self.rg_facts['sdn_access_group_id']
|
||||
!= self.rg_info.sdn_access_group_id
|
||||
)
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "sdn_access_group_id" failed: '
|
||||
'cannot change sdn_access_group_id for an existing resource '
|
||||
f'group ID {self.validated_rg_id}.'
|
||||
f'group ID {self.rg_id}.'
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
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
|
||||
|
||||
@@ -410,10 +509,15 @@ class decort_rg(DecortController):
|
||||
def run(self):
|
||||
amodule = self.amodule
|
||||
#amodule.check_mode=True
|
||||
if self.validated_rg_id > 0:
|
||||
if self.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]:
|
||||
if self.rg_id:
|
||||
if self.rg_info.status in [
|
||||
sdk_types.ResourceGroupStatus.MODELED,
|
||||
sdk_types.ResourceGroupStatus.DISABLING,
|
||||
sdk_types.ResourceGroupStatus.ENABLING,
|
||||
sdk_types.ResourceGroupStatus.DESTROYING,
|
||||
]:
|
||||
self.error()
|
||||
elif self.rg_facts['status'] in ("CREATED"):
|
||||
elif self.rg_info.status == sdk_types.ResourceGroupStatus.CREATED:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.destroy()
|
||||
elif amodule.params['state'] == "disabled":
|
||||
@@ -432,7 +536,7 @@ class decort_rg(DecortController):
|
||||
if amodule.params['def_netType'] is not None:
|
||||
self.setDefNet()
|
||||
|
||||
elif self.rg_facts['status'] == "DELETED":
|
||||
elif self.rg_info.status == sdk_types.ResourceGroupStatus.DELETED:
|
||||
if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True:
|
||||
self.destroy()
|
||||
elif (amodule.params['state'] == 'present'
|
||||
@@ -441,7 +545,7 @@ class decort_rg(DecortController):
|
||||
elif amodule.params['state'] == 'enabled':
|
||||
self.restore()
|
||||
self.enable()
|
||||
elif self.rg_facts['status'] in ("DISABLED"):
|
||||
elif self.rg_info.status == sdk_types.ResourceGroupStatus.DISABLED:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.destroy()
|
||||
elif amodule.params['state'] == ("enabled"):
|
||||
@@ -456,7 +560,8 @@ class decort_rg(DecortController):
|
||||
' the "rg_name" parameter was not specified.'
|
||||
)
|
||||
else:
|
||||
self.create()
|
||||
self.check_amodule_args_for_create()
|
||||
self.create()
|
||||
if amodule.params['access'] and not amodule.check_mode:
|
||||
self.access()
|
||||
elif amodule.params['state'] in ('disabled'):
|
||||
|
||||
@@ -70,7 +70,7 @@ class DecortRGList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
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:
|
||||
if (
|
||||
e.orig_exception.response
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
@@ -301,10 +301,11 @@ class DecortSecurityGroup(DecortController):
|
||||
match aparam_rules['mode']:
|
||||
case self.SecurityGroupRuleMode.delete.value:
|
||||
for rule in rules:
|
||||
self.security_group_detele_rule(
|
||||
security_group_id=self.id,
|
||||
rule_id=rule['id'],
|
||||
)
|
||||
self.sdk_checkmode(
|
||||
self.api.cloudapi.security_group.delete_rule)(
|
||||
security_group_id=self.id,
|
||||
rule_id=rule['id'],
|
||||
)
|
||||
case self.SecurityGroupRuleMode.match.value:
|
||||
for rule in rules:
|
||||
if rule.get('id') is None:
|
||||
@@ -318,10 +319,11 @@ class DecortSecurityGroup(DecortController):
|
||||
)
|
||||
rules_ids_to_delete = sg_rules_ids - aparam_rules_ids
|
||||
for rule_id in rules_ids_to_delete:
|
||||
self.security_group_detele_rule(
|
||||
security_group_id=self.id,
|
||||
rule_id=rule_id,
|
||||
)
|
||||
self.sdk_checkmode(
|
||||
self.api.cloudapi.security_group.delete_rule)(
|
||||
security_group_id=self.id,
|
||||
rule_id=rule_id,
|
||||
)
|
||||
case self.SecurityGroupRuleMode.update.value:
|
||||
for rule in rules:
|
||||
if rule.get('id') is None:
|
||||
|
||||
@@ -63,7 +63,7 @@ class DecortSecurityGroupList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class DecortStoragePolicy(DecortController):
|
||||
)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
if (
|
||||
e.orig_exception.response
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
|
||||
@@ -73,7 +73,7 @@ class DecortStoragePolicyList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class DecortTrunk(DecortController):
|
||||
)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
if (
|
||||
e.orig_exception.response
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
|
||||
@@ -54,7 +54,7 @@ class DecortTrunkList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
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.decort_utils import DecortController
|
||||
|
||||
from dynamix_sdk import exceptions as sdk_exceptions
|
||||
|
||||
|
||||
class DecortUser(DecortController):
|
||||
def __init__(self):
|
||||
@@ -43,16 +45,35 @@ class DecortUser(DecortController):
|
||||
self.facts = self.usermanager_whoami_result
|
||||
self.id = self.facts['name']
|
||||
|
||||
user_get = self.user_get(id=self.id)
|
||||
for key in ['emailaddresses', 'data']:
|
||||
self.facts[key] = user_get[key]
|
||||
try:
|
||||
user_model = self.api.cloudapi.user.get(user_name=self.id)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
if (
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='user',
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
raise e
|
||||
|
||||
self.facts.update(user_model.model_dump())
|
||||
|
||||
if self.aparams['resource_consumption']:
|
||||
self.facts.update(self.user_resource_consumption())
|
||||
self.facts.update(
|
||||
self.api.ca.user.get_resource_consumption().model_dump()
|
||||
)
|
||||
|
||||
# Delete duplicate self.facts['name']
|
||||
del self.facts['user_name']
|
||||
|
||||
if self.aparams['api_methods']:
|
||||
self.facts['api_methods'] = self.user_api_methods(id=self.id)
|
||||
|
||||
self.facts['api_methods'] = (
|
||||
self.api.cloudapi.user.api_list(user_name=self.id).model_dump()
|
||||
)
|
||||
search_string = self.aparams['objects_search']
|
||||
if search_string:
|
||||
self.facts['objects_search'] = self.user_objects_search(
|
||||
|
||||
@@ -20,7 +20,6 @@ class decort_vins(DecortController):
|
||||
|
||||
self.vins_id = 0
|
||||
self.vins_level = "" # "ID" if specified by ID, "RG" - at resource group, "ACC" - at account level
|
||||
vins_facts = None # will hold ViNS facts
|
||||
validated_rg_id = 0
|
||||
rg_facts = None # will hold RG facts
|
||||
validated_acc_id = 0
|
||||
@@ -28,15 +27,28 @@ class decort_vins(DecortController):
|
||||
if arg_amodule.params['vins_id']:
|
||||
# expect existing ViNS with the specified ID
|
||||
# This call to vins_find will abort the module if no ViNS with such ID is present
|
||||
self.vins_id, self.vins_facts = self.vins_find(arg_amodule.params['vins_id'],check_state=False)
|
||||
self.vins_id, self._vins_info = self.vins_find(
|
||||
arg_amodule.params['vins_id'],
|
||||
check_state=False,
|
||||
)
|
||||
if self.vins_id == 0:
|
||||
if arg_amodule.params['state'] == 'absent':
|
||||
self.exit()
|
||||
else:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_found(
|
||||
obj='VINS',
|
||||
id=arg_amodule.params['vins_id'],
|
||||
)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
if self._vins_info is None:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Specified ViNS ID {} not found.".format(arg_amodule.params['vins_id'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.vins_level = "ID"
|
||||
#raise Exception(self.vins_facts)
|
||||
validated_acc_id = self.vins_facts['accountId']
|
||||
validated_rg_id = self.vins_facts['rgId']
|
||||
validated_acc_id = self._vins_info.account_id
|
||||
validated_rg_id = self._vins_info.rg_id
|
||||
|
||||
elif arg_amodule.params['rg_id']:
|
||||
# expect ViNS @ RG level in the RG with specified ID
|
||||
@@ -44,14 +56,16 @@ class decort_vins(DecortController):
|
||||
# This call to rg_find will abort the module if no RG with such ID is present
|
||||
validated_rg_id, rg_facts = self.rg_find(0, # account ID set to 0 as we search for RG by RG ID
|
||||
arg_amodule.params['rg_id'], arg_rg_name="")
|
||||
validated_acc_id = rg_facts['accountId']
|
||||
|
||||
validated_acc_id = rg_facts.account_id
|
||||
|
||||
# This call to vins_find may return vins_id=0 if no ViNS found
|
||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=0,
|
||||
rg_id=arg_amodule.params['rg_id'],
|
||||
rg_facts=rg_facts,
|
||||
check_state=False)
|
||||
self.vins_id, self._vins_info = self.vins_find(
|
||||
vins_id=0,
|
||||
vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=0,
|
||||
rg_id=arg_amodule.params['rg_id'],
|
||||
check_state=False,
|
||||
)
|
||||
# TODO: add checks and setup ViNS presence flags accordingly
|
||||
pass
|
||||
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
|
||||
@@ -66,27 +80,39 @@ class decort_vins(DecortController):
|
||||
# expect ViNS @ RG level in the RG with specified name under specified account
|
||||
# RG with the specified name must be present under the account, otherwise abort the module
|
||||
validated_rg_id, rg_facts = self.rg_find(validated_acc_id, 0, arg_amodule.params['rg_name'])
|
||||
if (not validated_rg_id or
|
||||
rg_facts['status'] in ["DESTROYING", "DESTROYED", "DELETING", "DELETED", "DISABLING", "ENABLING"]):
|
||||
if (not validated_rg_id or rg_facts is None or
|
||||
rg_facts.status in [
|
||||
sdk_types.ResourceGroupStatus.DESTROYING,
|
||||
sdk_types.ResourceGroupStatus.DESTROYED,
|
||||
sdk_types.ResourceGroupStatus.DELETED,
|
||||
sdk_types.ResourceGroupStatus.DISABLING,
|
||||
sdk_types.ResourceGroupStatus.ENABLING,
|
||||
]
|
||||
):
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "RG name '{}' not found or has invalid state.".format(arg_amodule.params['rg_name'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
# This call to vins_find may return vins_id=0 if no ViNS with this name found under specified RG
|
||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=0, # set to 0, as we are looking for ViNS under RG
|
||||
rg_id=validated_rg_id,
|
||||
rg_facts=rg_facts,
|
||||
check_state=False)
|
||||
# (account_id) set to 0, as we are looking for ViNS under RG
|
||||
self.vins_id, self._vins_info = self.vins_find(
|
||||
vins_id=0,
|
||||
vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=0,
|
||||
rg_id=validated_rg_id,
|
||||
check_state=False,
|
||||
)
|
||||
self.vins_level = "RG"
|
||||
# TODO: add checks and setup ViNS presence flags accordingly
|
||||
else: # At this point we know for sure that rg_name="" and rg_id=0
|
||||
# So we expect ViNS @ account level
|
||||
# This call to vins_find may return vins_id=0 if no ViNS found
|
||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=validated_acc_id,
|
||||
rg_id=0,
|
||||
rg_facts=rg_facts,
|
||||
check_state=False)
|
||||
self.vins_id, self._vins_info = self.vins_find(
|
||||
vins_id=0,
|
||||
vins_name=arg_amodule.params['vins_name'],
|
||||
account_id=validated_acc_id,
|
||||
rg_id=0,
|
||||
check_state=False,
|
||||
)
|
||||
self.vins_level = "ACC"
|
||||
# TODO: add checks and setup ViNS presence flags accordingly
|
||||
else:
|
||||
@@ -106,73 +132,142 @@ class decort_vins(DecortController):
|
||||
self.rg_id = validated_rg_id
|
||||
self.acc_id = validated_acc_id
|
||||
|
||||
if self.vins_id and self.vins_facts['status'] != 'DESTROYED':
|
||||
if (
|
||||
self._vins_info
|
||||
and self._vins_info.status != sdk_types.VINSStatus.DESTROYED
|
||||
):
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
|
||||
return
|
||||
return
|
||||
|
||||
def create(self):
|
||||
self.vins_id = self.vins_provision(self.amodule.params['vins_name'],
|
||||
self.acc_id, self.rg_id,
|
||||
self.amodule.params['ipcidr'],
|
||||
self.amodule.params['ext_net_id'], self.amodule.params['ext_ip_addr'],
|
||||
self.amodule.params['description'],
|
||||
zone_id=self.amodule.params['zone_id'],
|
||||
security_group_mode = self.amodule.params['security_group_mode']
|
||||
if security_group_mode is None:
|
||||
security_group_mode = False
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='security_group_mode',
|
||||
default_value=security_group_mode
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
|
||||
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.amodule.params['mgmtaddr'] or self.amodule.params['connect_to']:
|
||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
||||
if self.vins_id:
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
if self.amodule.params['connect_to']:
|
||||
self.vins_update_ifaces(self.vins_facts,self.amodule.params['connect_to'],)
|
||||
self.vins_update_ifaces(
|
||||
self.vins_info,
|
||||
self.amodule.params['connect_to'],
|
||||
)
|
||||
if self.amodule.params['mgmtaddr']:
|
||||
self.vins_update_mgmt(self.vins_facts,self.amodule.params['mgmtaddr'])
|
||||
|
||||
self.vins_update_mgmt(
|
||||
self.vins_info,
|
||||
self.amodule.params['mgmtaddr'],
|
||||
)
|
||||
return
|
||||
def action(self,d_state='',restore=False):
|
||||
if restore == True:
|
||||
self.vins_restore(arg_vins_id=self.vins_id)
|
||||
self.vins_state(self.vins_facts, 'enabled')
|
||||
self.vins_facts['status'] = "ENABLED"
|
||||
self.vins_facts['VNFDev']['techStatus'] = "STARTED"
|
||||
|
||||
self.vins_update_extnet(self.vins_facts,
|
||||
self.amodule.params['ext_net_id'],
|
||||
self.amodule.params['ext_ip_addr'],
|
||||
)
|
||||
|
||||
if d_state == 'enabled' and self.vins_facts['status'] == "DISABLED":
|
||||
self.vins_state(self.vins_facts, d_state)
|
||||
self.vins_facts['status'] = "ENABLED"
|
||||
self.vins_facts['VNFDev']['techStatus'] = "STARTED"
|
||||
|
||||
def action(self, d_state='', restore=False):
|
||||
if restore:
|
||||
self.sdk_checkmode(self.api.cloudapi.vins.restore)(
|
||||
vins_id=self.vins_info.id,
|
||||
)
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
self.vins_state(self.vins_info, 'enabled')
|
||||
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
|
||||
if (
|
||||
self.amodule.params['ext_net_id'] is not None
|
||||
or self.amodule.params['ext_ip_addr'] is not None
|
||||
):
|
||||
self.vins_update_extnet(
|
||||
self.vins_info,
|
||||
self.amodule.params['ext_net_id'],
|
||||
self.amodule.params['ext_ip_addr'],
|
||||
)
|
||||
|
||||
if (
|
||||
d_state == 'enabled'
|
||||
and self.vins_info.status == sdk_types.VINSStatus.DISABLED
|
||||
):
|
||||
self.vins_state(self.vins_info, d_state)
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
d_state = ''
|
||||
|
||||
if self.vins_facts['status'] == "ENABLED" and self.vins_facts['VNFDev']['techStatus'] == "STARTED":
|
||||
self.vins_update_ifaces(self.vins_facts,
|
||||
self.amodule.params['connect_to'],
|
||||
)
|
||||
if (
|
||||
self.vins_info.status == sdk_types.VINSStatus.ENABLED
|
||||
and self.vins_info.vnfdev.tech_status == (
|
||||
sdk_types.VNFDevTechStatus.STARTED
|
||||
)
|
||||
):
|
||||
self.vins_update_ifaces(
|
||||
self.vins_info,
|
||||
self.amodule.params['connect_to'],
|
||||
)
|
||||
if self.result['changed']:
|
||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
||||
self.vins_update_mgmt(self.vins_facts,
|
||||
self.amodule.params['mgmtaddr'],
|
||||
)
|
||||
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
|
||||
self.vins_update_mgmt(
|
||||
self.vins_info,
|
||||
self.amodule.params['mgmtaddr'],
|
||||
)
|
||||
|
||||
if d_state != '':
|
||||
self.vins_state(self.vins_facts, d_state)
|
||||
|
||||
self.vins_state(self.vins_info, d_state)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.vins_facts['zoneId']:
|
||||
self.vins_migrate_to_zone(
|
||||
net_id=self.vins_id,
|
||||
if (
|
||||
aparam_zone_id is not None
|
||||
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,
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
def delete(self):
|
||||
self.vins_delete(self.vins_id, self.amodule.params['permanently'])
|
||||
self.vins_facts['status'] = 'DESTROYED'
|
||||
self.sdk_checkmode(self.api.cloudapi.vins.delete)(
|
||||
vins_id=self.vins_info.id,
|
||||
permanently=self.amodule.params['permanently'],
|
||||
)
|
||||
return
|
||||
|
||||
def nop(self):
|
||||
"""No operation (NOP) handler for ViNS management by decort_vins module.
|
||||
This function is intended to be called from the main switch construct of the module
|
||||
@@ -181,23 +276,27 @@ class decort_vins(DecortController):
|
||||
"""
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = False
|
||||
if self.vins_id:
|
||||
self.result['msg'] = ("No state change required for ViNS ID {} because of its "
|
||||
"current status '{}'.").format(self.vins_id, self.vins_facts['status'])
|
||||
if self._vins_info:
|
||||
self.result['msg'] = (
|
||||
f'No state change required for ViNS ID {self._vins_info.id} '
|
||||
f'because of its "current status "{self._vins_info.status}".'
|
||||
)
|
||||
else:
|
||||
self.result['msg'] = ("No state change to '{}' can be done for "
|
||||
"non-existent ViNS instance.").format(self.amodule.params['state'])
|
||||
return
|
||||
|
||||
def error(self):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
if self.vins_id:
|
||||
if self._vins_info:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("Invalid target state '{}' requested for ViNS ID {} in the "
|
||||
"current status '{}'").format(self.vins_id,
|
||||
self.amodule.params['state'],
|
||||
self.vins_facts['status'])
|
||||
self.result['msg'] = (
|
||||
f'Invalid target state "{self.amodule.params['state']}" '
|
||||
f'requested for ViNS ID {self._vins_info.id} in the '
|
||||
f'current status "{self._vins_info.status}"'
|
||||
)
|
||||
else:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
@@ -205,6 +304,7 @@ class decort_vins(DecortController):
|
||||
"ViNS name '{}'").format(self.amodule.params['state'],
|
||||
self.amodule.params['vins_name'])
|
||||
return
|
||||
|
||||
def package_facts(self, arg_check_mode=False):
|
||||
"""Package a dictionary of ViNS facts according to the decort_vins module specification.
|
||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||
@@ -222,40 +322,12 @@ class decort_vins(DecortController):
|
||||
# in check mode return immediately with the default values
|
||||
return ret_dict
|
||||
|
||||
if self.vins_facts is None:
|
||||
if self._vins_info is None:
|
||||
# if void facts provided - change state value to ABSENT and return
|
||||
ret_dict['state'] = "ABSENT"
|
||||
return ret_dict
|
||||
|
||||
ret_dict['id'] = self.vins_facts['id']
|
||||
ret_dict['name'] = self.vins_facts['name']
|
||||
ret_dict['state'] = self.vins_facts['status']
|
||||
ret_dict['account_id'] = self.vins_facts['accountId']
|
||||
ret_dict['rg_id'] = self.vins_facts['rgId']
|
||||
ret_dict['int_net_addr'] = self.vins_facts['network']
|
||||
ret_dict['gid'] = self.vins_facts['gid']
|
||||
custom_interfaces = list(filter(lambda i: i['type']=="CUSTOM",self.vins_facts['VNFDev']['interfaces']))
|
||||
if custom_interfaces:
|
||||
ret_dict['custom_net_addr'] = []
|
||||
for runner in custom_interfaces:
|
||||
ret_dict['custom_net_addr'].append(runner['ipAddress'])
|
||||
mgmt_interfaces = list(filter(lambda i: i['listenSsh'] and i['name']!="ens9",self.vins_facts['VNFDev']['interfaces']))
|
||||
if mgmt_interfaces:
|
||||
ret_dict['ssh_ipaddr'] = []
|
||||
for runner in mgmt_interfaces:
|
||||
ret_dict['ssh_ipaddr'].append(runner['ipAddress'])
|
||||
ret_dict['ssh_password'] = self.vins_facts['VNFDev']['config']['mgmt']['password']
|
||||
ret_dict['ssh_port'] = 9022
|
||||
if self.vins_facts['vnfs'].get('GW'):
|
||||
gw_config = self.vins_facts['vnfs']['GW']['config']
|
||||
ret_dict['ext_ip_addr'] = gw_config['ext_net_ip']
|
||||
ret_dict['ext_net_id'] = gw_config['ext_net_id']
|
||||
else:
|
||||
ret_dict['ext_ip_addr'] = ""
|
||||
ret_dict['ext_net_id'] = -1
|
||||
ret_dict['zone_id'] = self.vins_facts['zoneId']
|
||||
|
||||
return ret_dict
|
||||
return self._vins_info.model_dump()
|
||||
|
||||
|
||||
@property
|
||||
@@ -276,11 +348,9 @@ class decort_vins(DecortController):
|
||||
),
|
||||
ext_net_id=dict(
|
||||
type='int',
|
||||
default=-1,
|
||||
),
|
||||
ext_ip_addr=dict(
|
||||
type='str',
|
||||
default='',
|
||||
),
|
||||
ipcidr=dict(
|
||||
type='str',
|
||||
@@ -304,7 +374,6 @@ class decort_vins(DecortController):
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
default='present',
|
||||
choices=[
|
||||
'absent',
|
||||
'disabled',
|
||||
@@ -335,6 +404,12 @@ class decort_vins(DecortController):
|
||||
zone_id=dict(
|
||||
type=int,
|
||||
),
|
||||
enable_default_gateway=dict(
|
||||
type='bool',
|
||||
),
|
||||
security_group_mode=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
@@ -347,6 +422,34 @@ class decort_vins(DecortController):
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if (
|
||||
self.amodule.params['ext_ip_addr']
|
||||
and self.amodule.params['ext_net_id'] is None
|
||||
and self.vins_info.vnfs.gw is None
|
||||
):
|
||||
self.message(
|
||||
msg=(
|
||||
'Check for parameter "ext_net_id" failed: '
|
||||
'the "ext_net_id" parameter must be specified '
|
||||
'if the "ext_ip_addr" parameter is passed and '
|
||||
'VINS is not connected to an external network.'
|
||||
)
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
if (
|
||||
self.aparams['security_group_mode'] is not None
|
||||
and self.vins_info.security_group_mode != self.aparams['security_group_mode']
|
||||
):
|
||||
self.message(
|
||||
msg=(
|
||||
'Check for parameter "security_group_mode" failed: '
|
||||
'"security_group_mode" cannot be changed '
|
||||
'for existing ViNS'
|
||||
)
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@@ -384,36 +487,54 @@ class decort_vins(DecortController):
|
||||
# if cconfig_save is true, only config save without other updates
|
||||
vins_should_exist = False
|
||||
|
||||
if self.vins_id:
|
||||
if self._vins_info:
|
||||
vins_should_exist = True
|
||||
if self.vins_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING"]:
|
||||
if self._vins_info.status in [
|
||||
sdk_types.VINSStatus.MODELED,
|
||||
sdk_types.VINSStatus.DISABLING,
|
||||
sdk_types.VINSStatus.ENABLING,
|
||||
sdk_types.VINSStatus.DELETING,
|
||||
sdk_types.VINSStatus.DESTROYING,
|
||||
]:
|
||||
# error: nothing can be done to existing ViNS in the listed statii regardless of
|
||||
# the requested state
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = ("No change can be done for existing ViNS ID {} because of its current "
|
||||
"status '{}'").format(self.vins_id, self.vins_facts['status'])
|
||||
elif self.vins_facts['status'] == "DISABLED":
|
||||
self.result['msg'] = (
|
||||
f'No change can be done for existing '
|
||||
f'ViNS ID {self.vins_id} because of its '
|
||||
f'current status {self._vins_info.status}'
|
||||
)
|
||||
elif self._vins_info.status == sdk_types.VINSStatus.DISABLED:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.delete()
|
||||
vins_should_exist = False
|
||||
elif amodule.params['state'] in ('present', 'disabled'):
|
||||
elif (
|
||||
amodule.params['state'] is None
|
||||
or amodule.params['state'] in ('present', 'disabled')
|
||||
):
|
||||
# update ViNS, leave in disabled state
|
||||
self.action()
|
||||
elif amodule.params['state'] == 'enabled':
|
||||
# update ViNS and enable
|
||||
self.action('enabled')
|
||||
elif self.vins_facts['status'] in ["CREATED", "ENABLED"]:
|
||||
elif self._vins_info.status in [
|
||||
sdk_types.VINSStatus.CREATED,
|
||||
sdk_types.VINSStatus.ENABLED,
|
||||
]:
|
||||
if amodule.params['state'] == 'absent':
|
||||
self.delete()
|
||||
vins_should_exist = False
|
||||
elif amodule.params['state'] in ('present', 'enabled'):
|
||||
elif (
|
||||
amodule.params['state'] is None
|
||||
or amodule.params['state'] in ('present', 'enabled')
|
||||
):
|
||||
# update ViNS
|
||||
self.action()
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
# disable and update ViNS
|
||||
self.action('disabled')
|
||||
elif self.vins_facts['status'] == "DELETED":
|
||||
elif self._vins_info.status == sdk_types.VINSStatus.DELETED:
|
||||
if amodule.params['state'] in ['present', 'enabled']:
|
||||
# restore and enable
|
||||
self.action(restore=True)
|
||||
@@ -426,28 +547,48 @@ class decort_vins(DecortController):
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
self.error()
|
||||
vins_should_exist = False
|
||||
elif self.vins_facts['status'] == "DESTROYED":
|
||||
if amodule.params['state'] in ('present', 'enabled'):
|
||||
elif self._vins_info.status == sdk_types.VINSStatus.DESTROYED:
|
||||
state = amodule.params['state']
|
||||
if state is None:
|
||||
state = 'present'
|
||||
self.message(
|
||||
msg=(
|
||||
f'State not specified, '
|
||||
f'default value "{state}" will be used.'
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
if state in ('present', 'enabled'):
|
||||
# need to re-provision ViNS;
|
||||
self.create()
|
||||
vins_should_exist = True
|
||||
elif amodule.params['state'] == 'absent':
|
||||
elif state == 'absent':
|
||||
self.nop()
|
||||
vins_should_exist = False
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
elif state == 'disabled':
|
||||
self.error()
|
||||
else:
|
||||
state = amodule.params['state']
|
||||
if state is None:
|
||||
state = 'present'
|
||||
self.message(
|
||||
msg=(
|
||||
f'State not specified, '
|
||||
f'default value "{state}" will be used.'
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
# Preexisting ViNS was not found.
|
||||
vins_should_exist = False # we will change it back to True if ViNS is created or restored
|
||||
# If requested state is 'absent' - nothing to do
|
||||
if amodule.params['state'] == 'absent':
|
||||
if state == 'absent':
|
||||
self.nop()
|
||||
elif amodule.params['state'] in ('present', 'enabled'):
|
||||
elif state in ('present', 'enabled'):
|
||||
self.check_amodule_argument('vins_name')
|
||||
# as we already have account ID and RG ID we can create ViNS and get vins_id on success
|
||||
self.create()
|
||||
vins_should_exist = True
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
elif state == 'disabled':
|
||||
self.error()
|
||||
#
|
||||
# conditional switch end - complete module run
|
||||
@@ -457,7 +598,7 @@ class decort_vins(DecortController):
|
||||
else:
|
||||
# prepare ViNS facts to be returned as part of self.result and then call exit_json(...)
|
||||
if self.result['changed']:
|
||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
||||
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
|
||||
self.result['facts'] = self.package_facts(amodule.check_mode)
|
||||
amodule.exit_json(**self.result)
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class DecortVINSList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class decort_vm(DecortController):
|
||||
|
||||
validated_acc_id = 0
|
||||
validated_rg_id = 0
|
||||
validated_rg_facts = None
|
||||
validated_rg_model = None
|
||||
|
||||
self.vm_to_clone_id = 0
|
||||
self.vm_to_clone_info = None
|
||||
@@ -118,21 +118,21 @@ class decort_vm(DecortController):
|
||||
self.fail_json(**self.result)
|
||||
# fail the module -> exit
|
||||
# now validate RG
|
||||
validated_rg_id, validated_rg_facts = self.rg_find(validated_acc_id,
|
||||
validated_rg_id, validated_rg_model = self.rg_find(validated_acc_id,
|
||||
arg_amodule.params['rg_id'],
|
||||
arg_amodule.params['rg_name'])
|
||||
if not validated_rg_id:
|
||||
if not validated_rg_id or not validated_rg_model:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = "Cannot find RG ID {} / name '{}'.".format(arg_amodule.params['rg_id'],
|
||||
arg_amodule.params['rg_name'])
|
||||
self.fail_json(**self.result)
|
||||
self.amodule.fail_json(**self.result)
|
||||
# fail the module - exit
|
||||
|
||||
self.rg_id = validated_rg_id
|
||||
arg_amodule.params['rg_id'] = validated_rg_id
|
||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
||||
self.acc_id = validated_rg_facts['accountId']
|
||||
arg_amodule.params['rg_name'] = validated_rg_model.name
|
||||
self.acc_id = validated_rg_model.account_id
|
||||
|
||||
# at this point we are ready to locate Compute, and if anything fails now, then it must be
|
||||
# because this Compute does not exist or something goes wrong in the upstream API
|
||||
@@ -456,9 +456,9 @@ class decort_vm(DecortController):
|
||||
image_id=self.amodule.params['image_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
|
||||
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,
|
||||
# we create KVM VM in HALTED state.
|
||||
@@ -495,6 +495,10 @@ class decort_vm(DecortController):
|
||||
if numa_affinity is 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']
|
||||
if chipset is None:
|
||||
chipset = 'Q35'
|
||||
@@ -552,6 +556,8 @@ class decort_vm(DecortController):
|
||||
zone_id=self.aparams['zone_id'],
|
||||
storage_policy_id=self.aparams['storage_policy_id'],
|
||||
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
|
||||
|
||||
@@ -614,6 +620,12 @@ class decort_vm(DecortController):
|
||||
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
|
||||
_, self.comp_info, _ = self.compute_find(
|
||||
comp_id=self.comp_id,
|
||||
@@ -635,7 +647,11 @@ class decort_vm(DecortController):
|
||||
"""Compute destroy handler for VM management by decort_vm module.
|
||||
Note that this handler deletes the VM permanently together with all assigned disk resources.
|
||||
"""
|
||||
self.compute_delete(comp_id=self.comp_id, permanently=True)
|
||||
self.sdk_checkmode(self.api.ca.compute.delete)(
|
||||
vm_id=self.comp_id,
|
||||
detach_disks=True,
|
||||
permanently=True,
|
||||
)
|
||||
self.comp_id, self.comp_info, _ = self._compute_get_by_id(self.comp_id)
|
||||
return
|
||||
|
||||
@@ -669,6 +685,16 @@ class decort_vm(DecortController):
|
||||
**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:
|
||||
self.sdk_checkmode(self.api.cloudapi.compute.snapshot_rollback)(
|
||||
vm_id=self.comp_info['id'],
|
||||
@@ -693,10 +719,16 @@ class decort_vm(DecortController):
|
||||
aparam_disk_id = aparam_boot['disk_id']
|
||||
if aparam_disk_id is not None:
|
||||
for disk in self.comp_info['disks']:
|
||||
if disk['id'] == aparam_disk_id and disk['type'] != 'B':
|
||||
self.compute_boot_disk(
|
||||
comp_id=self.comp_info['id'],
|
||||
boot_disk=aparam_disk_id,
|
||||
if (
|
||||
disk['id'] == aparam_disk_id
|
||||
and not self.is_vm_boot_disk(
|
||||
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
|
||||
|
||||
@@ -734,7 +766,7 @@ class decort_vm(DecortController):
|
||||
_, image_facts = self.image_find(
|
||||
image_id=self.aparams['image_id'],
|
||||
)
|
||||
disk_size = image_facts['size']
|
||||
disk_size = image_facts['size_gb']
|
||||
|
||||
os_version = None
|
||||
if (
|
||||
@@ -772,8 +804,8 @@ class decort_vm(DecortController):
|
||||
compute_custom_fields = self.comp_info['custom_fields']
|
||||
if aparam_custom_fields['disable']:
|
||||
if compute_custom_fields is not None:
|
||||
self.compute_disable_custom_fields(
|
||||
compute_id=self.comp_info['id'],
|
||||
self.sdk_checkmode(self.api.ca.compute.delete_custom_fields)(
|
||||
vm_id=self.comp_info['id'],
|
||||
)
|
||||
else:
|
||||
if compute_custom_fields != aparam_custom_fields['fields']:
|
||||
@@ -784,8 +816,8 @@ class decort_vm(DecortController):
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.comp_info['zoneId']:
|
||||
self.compute_migrate_to_zone(
|
||||
compute_id=self.comp_id,
|
||||
self.sdk_checkmode(self.api.cloudapi.compute.migrate_to_zone)(
|
||||
vm_id=self.comp_id,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
@@ -796,15 +828,21 @@ class decort_vm(DecortController):
|
||||
aparam_guest_agent['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 (
|
||||
aparam_guest_agent['enabled'] is False
|
||||
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']:
|
||||
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']
|
||||
if aparam_guest_agent_exec is not None:
|
||||
@@ -838,6 +876,28 @@ class decort_vm(DecortController):
|
||||
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
|
||||
|
||||
@property
|
||||
@@ -858,6 +918,7 @@ class decort_vm(DecortController):
|
||||
'network_interface_naming': 'networkInterfaceNaming',
|
||||
'hot_resize': 'hotResize',
|
||||
'os_version': 'os_version',
|
||||
'clock_sync_mode': 'clock',
|
||||
}
|
||||
|
||||
def get_nested_value(
|
||||
@@ -934,11 +995,11 @@ class decort_vm(DecortController):
|
||||
tech_status="",
|
||||
account_id=0,
|
||||
rg_id=0,
|
||||
folder_id=0,
|
||||
username="",
|
||||
password="",
|
||||
public_ips=[], # direct IPs; this list can be empty
|
||||
private_ips=[], # IPs on ViNSes; usually, at least one IP is listed
|
||||
nat_ip="", # IP of the external ViNS interface; can be empty.
|
||||
tags={},
|
||||
chipset="",
|
||||
interfaces=[],
|
||||
@@ -968,6 +1029,7 @@ class decort_vm(DecortController):
|
||||
ret_dict['tech_status'] = self.comp_info['techStatus']
|
||||
ret_dict['account_id'] = self.comp_info['accountId']
|
||||
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']:
|
||||
ret_dict['tags'] = self.comp_info['tags']
|
||||
# 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']
|
||||
|
||||
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']:
|
||||
if iface['connType'] == "VXLAN": # This is ViNS connection
|
||||
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
|
||||
ret_dict['public_ips'].append(iface['ipAddress'])
|
||||
|
||||
@@ -1000,10 +1055,17 @@ class decort_vm(DecortController):
|
||||
|
||||
ret_dict['disks'] = self.comp_info['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
|
||||
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['interfaces'] = self.comp_info['interfaces']
|
||||
@@ -1073,6 +1135,14 @@ class decort_vm(DecortController):
|
||||
|
||||
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
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
@@ -1160,6 +1230,20 @@ class decort_vm(DecortController):
|
||||
|
||||
if self.check_aparam_zone_id() is False:
|
||||
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:
|
||||
check_errors = True
|
||||
@@ -1199,7 +1283,7 @@ class decort_vm(DecortController):
|
||||
)
|
||||
elif (
|
||||
aparam_storage_policy_id
|
||||
not in self.rg_info['storage_policy_ids']
|
||||
not in self.rg_info.storage_policy_ids
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -1215,6 +1299,17 @@ class decort_vm(DecortController):
|
||||
'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:
|
||||
self.exit(fail=True)
|
||||
|
||||
@@ -1312,6 +1407,9 @@ class decort_vm(DecortController):
|
||||
bus_num_hex=dict(
|
||||
type='str',
|
||||
),
|
||||
read_only=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
required_together=[
|
||||
('pci_slot_num_hex', 'bus_num_hex'),
|
||||
@@ -1319,6 +1417,9 @@ class decort_vm(DecortController):
|
||||
),
|
||||
),
|
||||
),
|
||||
folder_id=dict(
|
||||
type='int',
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
default=0,
|
||||
@@ -1371,6 +1472,9 @@ class decort_vm(DecortController):
|
||||
net_prefix=dict(
|
||||
type='int',
|
||||
),
|
||||
enable_default_gateway=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
required_if=[
|
||||
('type', 'VINS', ('id',)),
|
||||
@@ -1587,6 +1691,18 @@ class decort_vm(DecortController):
|
||||
abort_cloning=dict(
|
||||
type='bool',
|
||||
),
|
||||
cpu_alignment_profile_name=dict(
|
||||
type='str',
|
||||
),
|
||||
clock_sync_mode=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'default',
|
||||
'linux',
|
||||
'windows',
|
||||
'none',
|
||||
],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
@@ -1637,12 +1753,17 @@ class decort_vm(DecortController):
|
||||
f'disk {aparam_boot_disk_id} is not attached to '
|
||||
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']:
|
||||
case 'update':
|
||||
if (
|
||||
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
|
||||
self.message(
|
||||
@@ -1651,7 +1772,7 @@ class decort_vm(DecortController):
|
||||
f'to Compute ID {self.comp_id}.'
|
||||
)
|
||||
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
|
||||
self.message(
|
||||
f'Check for parameter "boot.disk_id" failed: '
|
||||
@@ -1659,7 +1780,7 @@ class decort_vm(DecortController):
|
||||
f'disks.ids'
|
||||
)
|
||||
case 'detach' | 'delete':
|
||||
if aparam_boot_disk_id in aparam_disks['ids']:
|
||||
if aparam_boot_disk_id in aparam_disk_ids:
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "boot.disk_id" failed: '
|
||||
@@ -1681,7 +1802,9 @@ class decort_vm(DecortController):
|
||||
if new_boot_disk_size is not None:
|
||||
boot_disk_size = 0
|
||||
for disk in self.comp_info['disks']:
|
||||
if disk['type'] == 'B':
|
||||
if self.is_vm_boot_disk(
|
||||
vm_chipset=self.comp_info['chipset'], vm_disk=disk,
|
||||
):
|
||||
boot_disk_size = disk['sizeMax']
|
||||
break
|
||||
else:
|
||||
@@ -1844,7 +1967,9 @@ class decort_vm(DecortController):
|
||||
aparam_disks_ids = [disk['id'] for disk in aparam_disks]
|
||||
comp_boot_disk_id = None
|
||||
for comp_disk in self.comp_info['disks']:
|
||||
if comp_disk['type'] == 'B':
|
||||
if self.is_vm_boot_disk(
|
||||
vm_chipset=self.comp_info['chipset'], vm_disk=comp_disk,
|
||||
):
|
||||
comp_boot_disk_id = comp_disk['id']
|
||||
break
|
||||
disks_to_detach = []
|
||||
@@ -1904,6 +2029,22 @@ class decort_vm(DecortController):
|
||||
|
||||
if self.check_aparam_zone_id() is False:
|
||||
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:
|
||||
check_errors = True
|
||||
@@ -1969,6 +2110,18 @@ class decort_vm(DecortController):
|
||||
'Only networks of type VINS, EXTNET, DPDK, SDN, TRUNK '
|
||||
'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:
|
||||
check_errors = True
|
||||
@@ -1979,6 +2132,27 @@ class decort_vm(DecortController):
|
||||
if self.check_aparam_image_id() is False:
|
||||
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:
|
||||
self.exit(fail=True)
|
||||
|
||||
@@ -2137,8 +2311,8 @@ class decort_vm(DecortController):
|
||||
vm_has_shared_sep_disk = False
|
||||
vm_disk_ids = [disk['id'] for disk in self.comp_info['disks']]
|
||||
for disk_id in vm_disk_ids:
|
||||
_, disk_info = self._disk_get_by_id(disk_id=disk_id)
|
||||
if disk_info['sepType'] == 'SHARED':
|
||||
disk_info = self._disk_get_by_id(disk_id=disk_id)
|
||||
if disk_info.sep_type == sdk_types.SEPType.SHARED:
|
||||
vm_has_shared_sep_disk = True
|
||||
break
|
||||
|
||||
@@ -2230,7 +2404,9 @@ class decort_vm(DecortController):
|
||||
if disk_redeploy:
|
||||
vm_has_boot_disk = False
|
||||
for disk in self.comp_info['disks']:
|
||||
if disk['type'] == 'B':
|
||||
if self.is_vm_boot_disk(
|
||||
vm_chipset=self.comp_info['chipset'], vm_disk=disk,
|
||||
):
|
||||
vm_has_boot_disk = True
|
||||
break
|
||||
if not vm_has_boot_disk:
|
||||
@@ -2341,7 +2517,9 @@ class decort_vm(DecortController):
|
||||
check_errors = False
|
||||
|
||||
# check if account has vm feature “trunk”
|
||||
if not self.check_account_vm_features(vm_feature=self.VMFeature.trunk):
|
||||
if not self.check_account_vm_features(
|
||||
vm_feature=sdk_types.VMFeature.TRUNK,
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
@@ -2349,7 +2527,7 @@ class decort_vm(DecortController):
|
||||
'trunk type networks '
|
||||
)
|
||||
# check if rg has vm feature “trunk”
|
||||
if not self.check_rg_vm_features(vm_feature=self.VMFeature.trunk):
|
||||
if not self.check_rg_vm_features(vm_feature=sdk_types.VMFeature.TRUNK):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
|
||||
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(
|
||||
type='str',
|
||||
),
|
||||
folder_id=dict(
|
||||
type='int',
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
),
|
||||
include_deleted=dict(
|
||||
type='bool',
|
||||
),
|
||||
include_descendants=dict(
|
||||
type='bool',
|
||||
),
|
||||
ip_addr=dict(
|
||||
type='str',
|
||||
),
|
||||
@@ -77,7 +83,7 @@ class DecortVMList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -129,8 +135,10 @@ class DecortVMList(DecortController):
|
||||
account_id=aparam_filter['account_id'],
|
||||
ext_net_id=aparam_filter['ext_net_id'],
|
||||
ext_net_name=aparam_filter['ext_net_name'],
|
||||
folder_id=aparam_filter['folder_id'],
|
||||
id=aparam_filter['id'],
|
||||
include_deleted=aparam_filter['include_deleted'] or False,
|
||||
include_descendants=aparam_filter['include_descendants'] or False,
|
||||
ip_addr=aparam_filter['ip_addr'],
|
||||
name=aparam_filter['name'],
|
||||
rg_id=aparam_filter['rg_id'],
|
||||
|
||||
@@ -10,6 +10,7 @@ description: See L(Module Documentation,https://repository.basistech.ru/BASIS/de
|
||||
import time
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
import dynamix_sdk.types as sdk_types
|
||||
|
||||
|
||||
class DecortVMSnapshot(DecortController):
|
||||
@@ -21,6 +22,7 @@ class DecortVMSnapshot(DecortController):
|
||||
self.vm_facts: dict
|
||||
self.aparams_label = self.aparams['label']
|
||||
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(
|
||||
comp_id=self.aparams_vm_id,
|
||||
@@ -50,11 +52,14 @@ class DecortVMSnapshot(DecortController):
|
||||
and self.aparams_label is not None
|
||||
and self.aparams_label not in self.vm_snapshot_labels
|
||||
):
|
||||
self.message(
|
||||
f'Snapshot {self.aparams_label} '
|
||||
f'not found for VM {self.aparams_vm_id}'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
if self.aparams['state'] == 'absent':
|
||||
self.exit()
|
||||
else:
|
||||
self.message(
|
||||
f'Snapshot {self.aparams_label} '
|
||||
f'not found for VM {self.aparams_vm_id}'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
@@ -79,6 +84,9 @@ class DecortVMSnapshot(DecortController):
|
||||
type='int',
|
||||
required=True,
|
||||
),
|
||||
with_memory=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
@@ -119,6 +127,7 @@ class DecortVMSnapshot(DecortController):
|
||||
label = self.new_snapshot_label or self.aparams_label
|
||||
for snapshot in self.vm_snapshots:
|
||||
if snapshot.label == label:
|
||||
self.vm_snapshot = snapshot
|
||||
self.facts = snapshot.model_dump()
|
||||
if self.aparams['usage']:
|
||||
self.facts['stored'] = self.get_snapshot_usage()
|
||||
@@ -137,9 +146,22 @@ class DecortVMSnapshot(DecortController):
|
||||
self.abort_merge()
|
||||
|
||||
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)(
|
||||
vm_id=self.aparams_vm_id,
|
||||
label=self.new_snapshot_label,
|
||||
with_memory=with_memory,
|
||||
)
|
||||
self.get_info(update_vm_snapshots=True)
|
||||
|
||||
@@ -151,10 +173,16 @@ class DecortVMSnapshot(DecortController):
|
||||
self.facts = {}
|
||||
|
||||
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,
|
||||
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()
|
||||
|
||||
def get_snapshot_usage(self) -> int:
|
||||
@@ -178,7 +206,33 @@ class DecortVMSnapshot(DecortController):
|
||||
'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:
|
||||
self.facts = {}
|
||||
self.exit(fail=True)
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class DecortZone(DecortController):
|
||||
zone_model = self.api.cloudapi.zone.get(id=self.id)
|
||||
except sdk_exceptions.RequestException as e:
|
||||
if (
|
||||
e.orig_exception.response
|
||||
e.orig_exception.response is not None
|
||||
and e.orig_exception.response.status_code == 404
|
||||
):
|
||||
self.message(
|
||||
|
||||
@@ -61,7 +61,7 @@ class DecortZoneList(DecortController):
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
default=100,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
ansible==11.6.0
|
||||
dacite==1.9.2
|
||||
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