diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3dddbd..ea715f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,200 @@
-# Список изменений в версии 12.0.3
+# Список изменений в версии 13.0.0
+
+## Изменено
+### Глобально
+| Идентификатор
задачи | Описание |
+| --- | --- |
+
+### Модуль decort_bservice
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1200 | Переименованы возвращаемые значения `state`, `techStatus` в `status`, `tech_status`. |
+
+### Модуль decort_disk_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1252 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_image_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1217 | У параметра `type` изменён тип с `str` на `list[str]`. |
+
+### Модуль decort_account_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1251 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_vm_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1253 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_flip_group_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1254 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_rg_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1256 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_audit_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1282 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_vins_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1257 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_zone_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1258 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_security_group_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1278 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_trunk_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1306 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
+
+### Модуль decort_storage_policy_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1307 | Для параметра `pagination.size` изменено значение по умолчанию с `50` на `100`. |
## Добавлено
+### Глобально
-## Удалено
+### Модуль decort_bservice
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1200 | Добавлены возвращаемые значения `account_name`, `base_domain`, `cpu_count_total`, `created_by`, `created_timestamp`, `deleted_by`, `deleted_timestamp`, `disk_size_total_gb`, `grid_id`, `guid`, `milestones`, `parent_service_id`, `parent_service_type`, `ram_size_total_mb`, `rg_name`, `snapshots`, `ssh_public_key`, `ssh_user_name`, `updated_by`, `updated_timestamp`, `user_managed`, `vms`. |
-## Исправлено
### Модуль decort_vm
| Идентификатор
задачи | Описание |
| --- | --- |
-| BANS-1377 | Модуль завершал работу ошибкой при передаче параметров `disks.objects.pci_slot_num_hex` и `disks.objects.bus_num_hex` в формате hex. |
+| BANS-1284 | Добавлено возвращаемое значение `interfaces.enable_default_gateway`. |
+| BANS-1222 | Добавлено возвращаемое значение `clock_sync_mode`. |
+| BANS-1229 | Добавлен параметр `cpu_alignment_profile_name`. |
+| BANS-1221 | Добавлено возвращаемое значение `cpu_alignment_profile`. |
+| BANS-1230 | Добавлен параметр `clock_sync_mode`. |
+| BANS-1238 | Добавлено возвращаемое значение `disks.read_only_mode`. |
+| BANS-1280 | Добавлен параметр `networks.enable_default_gateway`. |
+| BANS-1297 | Добавлено возвращаемое значение `disks.snapshots.pool_name`. |
+| BANS-1313 | Добавлено возвращаемое значение `resized_online`. |
+
+### Модуль decort_vm_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1223 | Добавлено возвращаемое значение `cpu_alignment_profile`. |
+| BANS-1224 | Добавлено возвращаемое значение `clock_sync_mode`. |
+| BANS-1239 | Добавлено возвращаемое значение `disks.read_only_mode`. |
+| BANS-1243 | Добавлено возвращаемое значение `snapshot_sets.vm`. |
+| BANS-1244 | Добавлено возвращаемое значение `snapshot_sets.memory_dump_image_id`. |
+| BANS-1283 | Добавлено возвращаемое значение `interfaces.enable_default_gateway`. |
+| BANS-1312 | Добавлено возвращаемое значение `resized_online`. |
+
+### Модуль decort_disk
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1218 | Добавлено возвращаемое значение `discard`. |
+| BANS-1240 | Добавлено возвращаемое значение `vms_with_read_only`. |
+| BANS-1298 | Добавлено возвращаемое значение `snapshots.pool_name`. |
+
+### Модуль decort_disk_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1219 | Добавлено возвращаемое значение `discard`. |
+| BANS-1241 | Добавлено возвращаемое значение `vms_with_read_only`. |
+
+### Модуль decort_zone_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1226 | Добавлено возвращаемое значение `cpu_alignment_profiles`. |
+| BANS-1287 | Добавлено возвращаемое значение `reserved_cpu`. |
+| BANS-1288 | Добавлено возвращаемое значение `reserved_ram`. |
+
+### Модуль decort_zone
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1225 | Добавлено возвращаемое значение `cpu_alignment_profiles`. |
+| BANS-1289 | Добавлено возвращаемое значение `reserved_cpu`. |
+| BANS-1290 | Добавлено возвращаемое значение `reserved_ram`. |
+
+### Модуль decort_vm_snapshot
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1223 | Добавлен параметр `with_memory`. |
+| BANS-1245 | Добавлено возвращаемое значение `memory_dump_image_id`. |
+| BANS-1246 | Добавлено возвращаемое значение `vm`. |
+
+### Модуль decort_vins
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1279 | Добавлен параметр `enable_default_gateway`. |
+| BANS-1285 | Добавлено возвращаемое значение `enable_default_gateway`. |
+| BANS-1286 | Добавлено возвращаемое значение `vnfdev.interfaces.enable_default_gateway`. |
+
+### Модуль decort_rg
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1301 | Добавлен параметр `enable_default_gateway`. |
+
+### Модуль decort_sdn_access_group
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1315 | Добавлено возвращаемое значение `default_security_policy.default_open_session_drop`. |
+
+### Модуль decort_sdn_access_group_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1314 | Добавлено возвращаемое значение `default_security_policy.default_open_session_drop`. |
+
+## Удалено
+### Модуль decort_disk
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1327 | Удалено возвращаемое значение `blkdiscard`. |
+
+### Модуль decort_disk_list
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1328 | Удалено возвращаемое значение `blkdiscard`. |
+
+## Исправлено
+### Модуль decort_bservice
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1203 | Модуль завершал работу ошибкой при `state: absent` для объекта, который не найден или безвозвратно удален. |
+| BANS-1205 | Модуль завершал работу ошибкой запроса к API при `state: enabled` для объекта, который безвозвратно удален. |
+| BANS-1207 | Модуль завершал работу ошибкой при создании объекта в режиме `Check mode`. |
+| BANS-1206 | Модуль завершал работу без ошибки при передачи `id` объекта, который безвозвратно удален, и `state: disabled`. |
+
+### Модуль decort_vm_snapshot
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1316 | Модуль завершал работу ошибкой при `state: absent` для снимка, который не найден или безвозвратно удален. |
+
+### Модуль decort_vm
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1330 | Модуль завершал работу ошибкой запроса к API при изменении образа без указания `storage_policy_id`. |
+
+### Модуль decort_image
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1338 | Модуль завершал работу ошибкой при `state: absent` для шаблонного образа, который не найден. |
+| BANS-1339 | Модуль без необходимости выполнял запрос к API `/cloudapi/image/delete` для шаблонного образа, который безвозвратно удален. |
+
+### Модуль decort_group
+| Идентификатор
задачи | Описание |
+| --- | --- |
+| BANS-1371 | Модуль завершал работу без ошибки при передаче `id` объекта, который не найден или безвозвратно удален, и `state: stopped`, `state: started` или без указания `state`. |
diff --git a/README.md b/README.md
index 5994f8e..0ba1de7 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
| Версия платформы | Версия модулей 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 |
diff --git a/library/decort_account_list.py b/library/decort_account_list.py
index fada940..0f0c97e 100644
--- a/library/decort_account_list.py
+++ b/library/decort_account_list.py
@@ -56,7 +56,7 @@ class DecortAccountList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_audit_list.py b/library/decort_audit_list.py
index f433789..07553d1 100644
--- a/library/decort_audit_list.py
+++ b/library/decort_audit_list.py
@@ -90,7 +90,7 @@ class DecortAuditList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_bservice.py b/library/decort_bservice.py
index 10402db..690ffb1 100644
--- a/library/decort_bservice.py
+++ b/library/decort_bservice.py
@@ -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,7 +35,7 @@ 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_model = self.rg_find(
@@ -55,14 +54,27 @@ class decort_bservice(DecortController):
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 '{}' "
@@ -112,7 +124,7 @@ class decort_bservice(DecortController):
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
@@ -155,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:
@@ -228,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'
)
)
@@ -244,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
@@ -270,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)
@@ -309,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']
@@ -329,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:
diff --git a/library/decort_disk.py b/library/decort_disk.py
index 8305f44..9599f66 100644
--- a/library/decort_disk.py
+++ b/library/decort_disk.py
@@ -63,7 +63,7 @@ class decort_disk(DecortController):
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
diff --git a/library/decort_disk_list.py b/library/decort_disk_list.py
index 1ccb0f9..073646d 100644
--- a/library/decort_disk_list.py
+++ b/library/decort_disk_list.py
@@ -77,7 +77,7 @@ class DecortDiskList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_flip_group_list.py b/library/decort_flip_group_list.py
index 63bec52..ba9da1c 100644
--- a/library/decort_flip_group_list.py
+++ b/library/decort_flip_group_list.py
@@ -75,7 +75,7 @@ class DecortFlipGroupList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_group.py b/library/decort_group.py
index edb24a1..d233250 100644
--- a/library/decort_group.py
+++ b/library/decort_group.py
@@ -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
diff --git a/library/decort_image.py b/library/decort_image.py
index 7d45177..b217d2a 100644
--- a/library/decort_image.py
+++ b/library/decort_image.py
@@ -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,
@@ -189,9 +209,9 @@ class decort_image(DecortController):
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)
@@ -250,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']:
@@ -268,13 +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['linksTo']
+ ret_dict['links_to'] = arg_image_facts['links_to']
return ret_dict
@property
@@ -502,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'],
)
@@ -510,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']:
@@ -530,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:
@@ -539,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'],
)
diff --git a/library/decort_image_list.py b/library/decort_image_list.py
index 918b909..4c5b6f1 100644
--- a/library/decort_image_list.py
+++ b/library/decort_image_list.py
@@ -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'],
diff --git a/library/decort_k8s.py b/library/decort_k8s.py
index 37ad35e..8b896f3 100644
--- a/library/decort_k8s.py
+++ b/library/decort_k8s.py
@@ -104,7 +104,9 @@ class decort_k8s(DecortController):
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.k8s_getConfig()
+ self.k8s_info['config'] = self.api.cloudapi.k8s.get_config(
+ k8s_id=self.k8s_id,
+ )
return self.k8s_info
def nop(self):
@@ -269,7 +271,7 @@ class decort_k8s(DecortController):
aparam_zone_id is not None
and aparam_zone_id != self.k8s_info['zone_id']
):
- self.k8s_migrate_to_zone(
+ self.sdk_checkmode(self.api.cloudapi.k8s.migrate_to_zone)(
k8s_id=self.k8s_id,
zone_id=aparam_zone_id,
)
diff --git a/library/decort_lb.py b/library/decort_lb.py
index b2bad74..a7e3087 100644
--- a/library/decort_lb.py
+++ b/library/decort_lb.py
@@ -184,7 +184,7 @@ class decort_lb(DecortController):
aparam_zone_id is not None
and aparam_zone_id != self.lb_info.zone_id
):
- self.lb_migrate_to_zone(
+ self.sdk_checkmode(self.api.cloudapi.lb.migrate_to_zone)(
lb_id=self.lb_info.id,
zone_id=aparam_zone_id,
)
diff --git a/library/decort_rg.py b/library/decort_rg.py
index 6dd914c..11369fc 100644
--- a/library/decort_rg.py
+++ b/library/decort_rg.py
@@ -234,6 +234,7 @@ 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.rg_id:
@@ -410,6 +411,9 @@ class decort_rg(DecortController):
sdn_access_group_id=dict(
type='str',
),
+ enable_default_gateway=dict(
+ type='bool',
+ ),
),
supports_check_mode=True,
)
@@ -431,6 +435,66 @@ class decort_rg(DecortController):
)
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
+
if check_errors:
self.exit(fail=True)
@@ -496,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'):
diff --git a/library/decort_rg_list.py b/library/decort_rg_list.py
index 7a1350d..50c7256 100644
--- a/library/decort_rg_list.py
+++ b/library/decort_rg_list.py
@@ -70,7 +70,7 @@ class DecortRGList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_security_group.py b/library/decort_security_group.py
index 6a6b5bb..8d97d43 100644
--- a/library/decort_security_group.py
+++ b/library/decort_security_group.py
@@ -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:
diff --git a/library/decort_security_group_list.py b/library/decort_security_group_list.py
index d7bc22f..799e72e 100644
--- a/library/decort_security_group_list.py
+++ b/library/decort_security_group_list.py
@@ -63,7 +63,7 @@ class DecortSecurityGroupList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_storage_policy_list.py b/library/decort_storage_policy_list.py
index 6259523..37ad303 100644
--- a/library/decort_storage_policy_list.py
+++ b/library/decort_storage_policy_list.py
@@ -73,7 +73,7 @@ class DecortStoragePolicyList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_trunk_list.py b/library/decort_trunk_list.py
index b5b0e6e..483391c 100644
--- a/library/decort_trunk_list.py
+++ b/library/decort_trunk_list.py
@@ -54,7 +54,7 @@ class DecortTrunkList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_vins.py b/library/decort_vins.py
index d3cf6d9..3b6f7fe 100644
--- a/library/decort_vins.py
+++ b/library/decort_vins.py
@@ -154,6 +154,17 @@ class decort_vins(DecortController):
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,
@@ -164,6 +175,7 @@ class decort_vins(DecortController):
desc=self.amodule.params['description'],
zone_id=self.amodule.params['zone_id'],
security_group_mode=security_group_mode,
+ enable_default_gateway=enable_default_gateway,
)
if self.vins_id:
self._vins_info = self._vins_get_by_id(vins_id=self.vins_id)
@@ -233,11 +245,20 @@ class decort_vins(DecortController):
aparam_zone_id is not None
and aparam_zone_id != self.vins_info.zone_id
):
- self.vins_migrate_to_zone(
- net_id=self.vins_info.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):
@@ -383,6 +404,9 @@ class decort_vins(DecortController):
zone_id=dict(
type=int,
),
+ enable_default_gateway=dict(
+ type='bool',
+ ),
security_group_mode=dict(
type='bool',
),
diff --git a/library/decort_vins_list.py b/library/decort_vins_list.py
index bb5a3c5..b36d16a 100644
--- a/library/decort_vins_list.py
+++ b/library/decort_vins_list.py
@@ -67,7 +67,7 @@ class DecortVINSList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_vm.py b/library/decort_vm.py
index cb64ac8..b087cc7 100644
--- a/library/decort_vm.py
+++ b/library/decort_vm.py
@@ -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
@@ -704,9 +710,9 @@ class decort_vm(DecortController):
vm_disk=disk,
)
):
- self.compute_boot_disk(
- comp_id=self.comp_info['id'],
- boot_disk=aparam_disk_id,
+ self.sdk_checkmode(self.api.cloudapi.compute.boot_disk_set)(
+ vm_id=self.comp_info['id'],
+ disk_id=aparam_disk_id,
)
break
@@ -744,7 +750,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 (
@@ -782,8 +788,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']:
@@ -794,8 +800,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,
)
@@ -806,15 +812,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:
@@ -848,6 +860,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
@@ -868,6 +902,7 @@ class decort_vm(DecortController):
'network_interface_naming': 'networkInterfaceNaming',
'hot_resize': 'hotResize',
'os_version': 'os_version',
+ 'clock_sync_mode': 'clock',
}
def get_nested_value(
@@ -948,7 +983,6 @@ class decort_vm(DecortController):
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=[],
@@ -987,16 +1021,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'])
@@ -1010,6 +1037,7 @@ class decort_vm(DecortController):
ret_dict['disks'] = self.comp_info['disks']
for disk in ret_dict['disks']:
+ disk['read_only_mode'] = disk.pop('read_only')
if self.is_vm_boot_disk(
vm_chipset=self.comp_info['chipset'],
vm_disk=disk,
@@ -1017,6 +1045,9 @@ class decort_vm(DecortController):
# 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']
@@ -1088,6 +1119,12 @@ class decort_vm(DecortController):
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):
@@ -1175,6 +1212,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
@@ -1230,6 +1281,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)
@@ -1386,6 +1448,9 @@ class decort_vm(DecortController):
net_prefix=dict(
type='int',
),
+ enable_default_gateway=dict(
+ type='bool',
+ ),
),
required_if=[
('type', 'VINS', ('id',)),
@@ -1602,6 +1667,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=[
@@ -1928,6 +2005,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
@@ -1993,6 +2086,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
@@ -2003,6 +2108,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)
diff --git a/library/decort_vm_list.py b/library/decort_vm_list.py
index 568b77b..7ee8730 100644
--- a/library/decort_vm_list.py
+++ b/library/decort_vm_list.py
@@ -77,7 +77,7 @@ class DecortVMList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/library/decort_vm_snapshot.py b/library/decort_vm_snapshot.py
index f2c4c4d..316aa0b 100644
--- a/library/decort_vm_snapshot.py
+++ b/library/decort_vm_snapshot.py
@@ -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)
diff --git a/library/decort_zone_list.py b/library/decort_zone_list.py
index 78d2d3f..65ba521 100644
--- a/library/decort_zone_list.py
+++ b/library/decort_zone_list.py
@@ -61,7 +61,7 @@ class DecortZoneList(DecortController):
),
size=dict(
type='int',
- default=50,
+ default=100,
),
),
),
diff --git a/module_utils/decort_utils.py b/module_utils/decort_utils.py
index 9905851..a8ce636 100644
--- a/module_utils/decort_utils.py
+++ b/module_utils/decort_utils.py
@@ -3,6 +3,7 @@ from datetime import datetime
from enum import Enum
from functools import wraps
from importlib.metadata import version as get_package_version
+import inspect
import json
import re
from typing import (
@@ -22,6 +23,7 @@ from dynamix_sdk import BVSAuth, DECS3OAuth, Dynamix
from dynamix_sdk import __name__ as SDK_PACKAGE_NAME
from dynamix_sdk import exceptions as sdk_exceptions
import dynamix_sdk.api._nested as _nested
+from dynamix_sdk.base import APIFunctionImpl
import dynamix_sdk.types as sdk_types
import requests
import urllib3
@@ -46,11 +48,13 @@ class DecortController(object):
_vins_info: None | sdk_types.CloudapiVinsGetResultModel = None
k8s_id: None | int = None
_k8s_info: None | dict = None
+ bservice_id: None | int = None
+ _bservice_info: None | dict = None
_api: sdk_types.API | None = None
_usermanager_whoami_result: None | dict = None
- ANSIBLE_MODULES_VERSION = '12.0.3'
- COMPATIBLE_SDK_MINOR_VERSION = '1.5'
+ ANSIBLE_MODULES_VERSION = '13.0.0'
+ COMPATIBLE_SDK_MINOR_VERSION = '2.0'
VM_RESIZE_NOT = 0
VM_RESIZE_DOWN = 1
@@ -221,6 +225,23 @@ class DecortController(object):
return msg
+ @staticmethod
+ def sdk_function_in_check_mode(
+ func_name: str,
+ func_args: tuple,
+ func_kwargs: dict,
+ ) -> str:
+ msg = (
+ f'SDK function "{func_name}" was called in check mode'
+ f' with arguments:'
+ )
+ for arg in func_args:
+ msg += f'\n {arg}'
+ for k, v in func_kwargs.items():
+ msg += f'\n {k}={v}'
+
+ return msg
+
@staticmethod
def default_value_used(param_name: str, default_value: Any) -> str:
return (
@@ -410,6 +431,14 @@ class DecortController(object):
raise TypeError
self._k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
return self._k8s_info
+
+ @property
+ def bservice_info(self) -> dict:
+ if self._bservice_info is None:
+ if not isinstance(self.bservice_id, int):
+ raise TypeError
+ self._bservice_info = self.bservice_get_by_id(bs_id=self.bservice_id)
+ return self._bservice_info
@property
def usermanager_whoami_result(self) -> dict:
@@ -501,8 +530,15 @@ class DecortController(object):
"""
@wraps(orig_f)
def new_f(*args, **kwargs):
- name = orig_f.__name__.replace('__', '.')
- self.result['waypoints'] += f' -> {name}'
+ if not inspect.ismethod(orig_f):
+ raise TypeError
+
+ inst = orig_f.__self__
+ if not isinstance(inst, APIFunctionImpl):
+ raise TypeError
+ api_func_impl = inst
+
+ self.result['waypoints'] += f' -> {api_func_impl.func_name}'
return orig_f(*args, **kwargs)
return new_f
@@ -528,29 +564,35 @@ class DecortController(object):
return orig_f(self, *args, **kwargs)
return new_f
- def sdk_checkmode(self, orig_f: Callable[P, R]) -> Callable[P, R | None]:
+ def sdk_checkmode(
+ self,
+ api_func_impl: Callable[P, R],
+ ) -> Callable[P, R | None]:
"""
- A decorator for SDK methods that should not be executed in
+ A decorator for SDK functions that should not be executed in
Ansible Check Mode.
- Instead of executing these methods, a message will be added
- with the method name and the arguments with which it was called.
+ Instead of executing these functions, a message will be added
+ with the function name and the arguments with which it was called.
"""
- @wraps(orig_f)
- def new_f(*args, **kwargs):
+ if not isinstance(api_func_impl, APIFunctionImpl):
+ raise TypeError
+
+ @wraps(api_func_impl.__call__)
+ def wrapper(*args, **kwargs):
if self.amodule.check_mode:
self.message(
- self.MESSAGES.method_in_check_mode(
- method_name=orig_f.__name__.replace('__', '.'),
- method_args=args,
- method_kwargs=kwargs,
+ self.MESSAGES.sdk_function_in_check_mode(
+ func_name=api_func_impl.func_name,
+ func_args=args,
+ func_kwargs=kwargs,
)
)
return None
else:
self.set_changed()
- return orig_f(*args, **kwargs)
+ return api_func_impl(*args, **kwargs)
- return new_f
+ return wrapper
@staticmethod
def handle_sdk_exceptions(f: Callable[P, R]) -> Callable[P, R]:
@@ -877,13 +919,23 @@ class DecortController(object):
if accept_json_response:
http_headers['Accept'] = 'application/json'
+ def remove_none(obj):
+ if isinstance(obj, dict):
+ return {
+ k: remove_none(v) for k, v in obj.items()
+ if v is not None
+ }
+ elif isinstance(obj, list):
+ return [remove_none(x) for x in obj]
+ return obj
+
while retry_counter > 0:
try:
api_resp = arg_req_function(
req_url,
files=arg_files,
params=arg_params,
- json=arg_json_body,
+ json=remove_none(obj=arg_json_body),
headers=http_headers,
verify=self.verify_ssl,
)
@@ -929,6 +981,48 @@ class DecortController(object):
self.amodule.fail_json(**self.result)
return None
+ def paginate_sdk_call(
+ self,
+ api_func_impl: Callable[P, R],
+ ) -> Callable[P, list]:
+ if not isinstance(api_func_impl, APIFunctionImpl):
+ raise TypeError
+
+ @wraps(api_func_impl.__call__)
+ def wrapper(*args, **kwargs):
+ page = 1
+ obj_list = []
+ while True:
+ result = api_func_impl(
+ *args,
+ page_number=page,
+ **kwargs,
+ )
+ obj_list.extend(result.data)
+ if len(obj_list) >= result.entry_count:
+ break
+ page += 1
+ return obj_list
+
+ return wrapper
+
+ def paginate_api_call(
+ self,
+ api_name: str,
+ params: dict[str, Any],
+ page_size: int = 100,
+ ) -> list[dict[str, Any]]:
+ api_params = dict(params, page=1, size=page_size)
+ obj_list: list[dict[str, Any]] = []
+ while True:
+ api_resp = self.decort_api_call(requests.post, api_name, api_params)
+ result = api_resp.json()
+ obj_list.extend(result['data'])
+ if len(obj_list) >= result['entryCount']:
+ break
+ api_params['page'] += 1
+ return obj_list
+
@waypoint
def user_objects_search(self, search_string: str) -> list[dict]:
"""
@@ -1166,16 +1260,10 @@ class DecortController(object):
disks_to_attach.append(aparam_disk)
for disk in disks_to_detach:
- api_params = {
- 'computeId': comp_dict['id'],
- 'diskId': disk['id'],
- }
- self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/diskDetach',
- arg_params=api_params,
+ self.sdk_checkmode(self.api.cloudapi.compute.disk_detach)(
+ vm_id=comp_dict['id'],
+ disk_id=disk['id'],
)
- self.set_changed()
for disk in disks_to_attach:
pci_slot_num = disk['pci_slot_num_hex']
@@ -1186,44 +1274,18 @@ class DecortController(object):
if bus_num is not None:
bus_num = hex(self._parse_hex(bus_num))
- api_params = {
- 'computeId': comp_dict['id'],
- 'diskId': disk['id'],
- 'pci_slot': pci_slot_num,
- 'bus_number': bus_num,
- }
- self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/diskAttach',
- arg_params=api_params,
+ self.sdk_checkmode(self.api.cloudapi.compute.disk_attach)(
+ vm_id=comp_dict['id'],
+ disk_id=disk['id'],
+ pci_slot_num_hex=pci_slot_num,
+ bus_num_hex=bus_num,
)
- self.set_changed()
for disk in disks_to_delete:
- api_params = {
- 'computeId': comp_dict['id'],
- 'diskId': disk['id'],
- }
- self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/diskDel',
- arg_params=api_params,
+ self.sdk_checkmode(self.api.cloudapi.compute.disk_del)(
+ vm_id=comp_dict['id'],
+ disk_id=disk['id'],
)
- self.set_changed()
-
- @waypoint
- @checkmode
- def compute_boot_disk(self, comp_id: int, boot_disk: int):
- api_params = {
- 'computeId': comp_id,
- 'diskId': boot_disk,
- }
- self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/bootDiskSet',
- arg_params=api_params,
- )
- self.set_changed()
def _compute_get_by_id(
self,
@@ -1269,8 +1331,8 @@ class DecortController(object):
ret_comp_dict['custom_fields'] = custom_fields
if need_console_url and ret_comp_dict['techStatus'] == 'STARTED':
- console_url = self.compute_get_console_url(
- compute_id=ret_comp_id,
+ console_url = self.api.cloudapi.compute.get_console_url(
+ vm_id=ret_comp_id,
)
ret_comp_dict['console_url'] = console_url
@@ -1355,21 +1417,15 @@ class DecortController(object):
self.amodule.fail_json(**self.result)
# fail the module - exit
- api_params = dict(includedeleted=True, )
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/list", api_params)
- if api_resp.status_code == 200:
- comp_list = json.loads(api_resp.content.decode('utf8'))
- else:
- self.result['failed'] = True
- self.result['msg'] = ("compute_find(): failed to get list Computes. HTTP code {}, "
- "response {}.").format(api_resp.status_code, api_resp.reason)
- self.amodule.fail_json(**self.result)
- # fail the module - exit
+ comp_list = self.paginate_api_call(
+ "/restmachine/cloudapi/compute/list",
+ dict(includedeleted=True),
+ )
# if we have validated RG ID at this point, look up Compute by name in this RG
# rg.vms list contains IDs of compute instances registered with this RG until compute is
# destroyed. So we may see here computes in "active" and DELETED states.
- for runner in comp_list['data']:
+ for runner in comp_list:
if runner['name'] == comp_name and runner['rgId'] == rg_id:
if not check_state or runner['status'] not in COMP_INVALID_STATES:
ret_comp_id = runner['id']
@@ -1492,6 +1548,8 @@ class DecortController(object):
zone_id: None | int = None,
storage_policy_id: None | int = None,
os_version: None | str = None,
+ cpu_alignment_profile_name: None | str = None,
+ clock_sync_mode: Literal['default', 'linux', 'windows', 'none'] = 'default', # noqa: E501
):
"""Manage KVM VM provisioning. To remove existing KVM VM compute instance use compute_remove method,
to resize use compute_resize, to manage power state use compute_powerstate method.
@@ -1518,47 +1576,59 @@ class DecortController(object):
"was requested.").format(comp_name, rg_id)
return 0
- api_params = {
- 'rgId': rg_id,
- 'name': comp_name,
- 'cpu': cpu,
- 'ram': ram,
- 'bootDisk': boot_disk_size,
- 'sepId': sep_id,
- 'pool': pool_name,
- 'interfaces': '[]', # we create VM without any network connections
- 'chipset': chipset,
- 'withoutBootDisk': not boot_disk_size,
- 'preferredCpu': preferred_cpu_cores,
- 'zoneId': zone_id,
- 'storage_policy_id': storage_policy_id,
- 'os_version': os_version,
- 'cpupin': cpu_pin,
- 'hpBacked': hp_backed,
- 'numaAffinity': numa_affinity,
- }
- if description:
- api_params['desc'] = description
-
if not image_id:
- api_url = '/restmachine/cloudapi/kvmx86/createBlank'
- api_params['bootType'] = boot_mode
- api_params['loaderType'] = boot_loader_type
- api_params['networkInterfaceNaming'] = network_interface_naming
- api_params['hotResize'] = hot_resize
+ ret_vm_id = self.sdk_checkmode(self.api.cloudapi.kvmx86.create_blank)(
+ rg_id=rg_id,
+ name=comp_name,
+ cpu_count=cpu,
+ ram_size_mb=ram,
+ storage_policy_id=storage_policy_id,
+ boot_disk_size_gb=boot_disk_size,
+ chipset=chipset,
+ cpu_pin=cpu_pin,
+ description=description or None,
+ hp_backed=hp_backed,
+ interfaces=[], # we create VM without any network connections
+ numa_affinity=numa_affinity,
+ preferred_cpu_cores=preferred_cpu_cores,
+ sep_id=sep_id,
+ sep_pool_name=pool_name,
+ without_boot_disk=not boot_disk_size,
+ zone_id=zone_id,
+ os_version=os_version,
+ clock_sync_mode=clock_sync_mode,
+ cpu_alignment_profile_name=cpu_alignment_profile_name,
+ boot_loader_type=boot_loader_type,
+ boot_mode=boot_mode,
+ network_interface_naming=network_interface_naming,
+ hot_resize=hot_resize,
+ )
else:
- api_url = '/restmachine/cloudapi/kvmx86/create'
- api_params['imageId'] = image_id
- api_params['start'] = start_on_create
-
- if userdata:
- api_params['userdata'] = json.dumps(userdata) # we need to pass a string object as "userdata"
-
- api_resp = self.decort_api_call(requests.post, api_url, api_params)
- # On success the above call will return here. On error it will abort execution by calling fail_json.
- self.result['failed'] = False
- self.result['changed'] = True
- ret_vm_id = int(api_resp.content)
+ ret_vm_id = self.sdk_checkmode(self.api.cloudapi.kvmx86.create)(
+ rg_id=rg_id,
+ name=comp_name,
+ cpu_count=cpu,
+ ram_size_mb=ram,
+ storage_policy_id=storage_policy_id,
+ boot_disk_size_gb=boot_disk_size,
+ chipset=chipset,
+ ci_user_data=userdata,
+ cpu_pin=cpu_pin,
+ description=description or None,
+ hp_backed=hp_backed,
+ image_id=image_id,
+ interfaces=[], # we create VM without any network connections
+ numa_affinity=numa_affinity,
+ preferred_cpu_cores=preferred_cpu_cores,
+ sep_id=sep_id,
+ sep_pool_name=pool_name,
+ start=start_on_create,
+ without_boot_disk=not boot_disk_size,
+ zone_id=zone_id,
+ os_version=os_version,
+ clock_sync_mode=clock_sync_mode,
+ cpu_alignment_profile_name=cpu_alignment_profile_name,
+ )
return ret_vm_id
@@ -1642,6 +1712,19 @@ class DecortController(object):
if iface and net_prefix != iface['netMask']:
net_key = f'{net_key}_new-net-prefix'
+ if net_type == self.VMNetType.VINS.value:
+ net_enable_default_gateway = net['enable_default_gateway']
+ if net_enable_default_gateway is not None:
+ iface = ifaces_dict.get(net_key)
+ if (
+ iface
+ and (
+ net_enable_default_gateway
+ != iface['enable_default_gateway']
+ )
+ ):
+ net_key = f'{net_key}_new-default-gateway'
+
new_nets_dict[net_key] = net
# The networks that no need to be disconnected or reconnected
@@ -1730,16 +1813,11 @@ class DecortController(object):
# Detaching networks
for iface in ifaces_for_delete:
- self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/netDetach',
- arg_params={
- 'computeId': vm_id,
- 'mac': iface['mac'],
- 'ipAddr': iface['ipAddress'] or None,
- },
+ self.sdk_checkmode(self.api.cloudapi.compute.net_detach)(
+ vm_id=vm_id,
+ mac=iface['mac'],
+ ip_addr=iface['ipAddress'] or None,
)
- self.set_changed()
# Attaching networks
for net in nets_for_attach:
@@ -1789,6 +1867,10 @@ class DecortController(object):
net.get('net_prefix')
or old_iface.get('net_prefix')
),
+ 'enable_default_gateway': net.get(
+ 'enable_default_gateway',
+ old_iface.get('enable_default_gateway')
+ ),
}
if net['type'] == self.VMNetType.SDN.value:
@@ -2229,6 +2311,7 @@ class DecortController(object):
network_interface_naming: None | Literal['eth', 'ens'] = None,
hot_resize: None | bool = None,
os_version: None | str = None,
+ clock_sync_mode: None | str = None,
):
OBJ = 'compute'
@@ -2252,6 +2335,7 @@ class DecortController(object):
'networkInterfaceNaming': network_interface_naming,
'hotResize': hot_resize,
'os_version': os_version,
+ 'clock': clock_sync_mode,
},
)
@@ -2271,6 +2355,7 @@ class DecortController(object):
'network_interface_naming': network_interface_naming,
'hot_resize': hot_resize,
'os_version': os_version,
+ 'clock_sync_mode': clock_sync_mode,
}
for param, value in params_to_check.items():
if value is not None:
@@ -2290,28 +2375,9 @@ class DecortController(object):
compute_id: int,
custom_fields: dict,
):
- self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/setCustomFields',
- arg_params={
- 'computeId': compute_id,
- 'customFields': json.dumps(custom_fields),
- },
- )
- self.set_changed()
-
- @waypoint
- @checkmode
- def compute_disable_custom_fields(
- self,
- compute_id: int,
- ):
- self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/deleteCustomFields',
- arg_params={
- 'computeId': compute_id,
- },
+ self.api.ca.compute.set_custom_fields(
+ vm_id=compute_id,
+ custom_fields=str(custom_fields),
)
self.set_changed()
@@ -2424,31 +2490,6 @@ class DecortController(object):
time.sleep(sleep_interval)
return clone_id
- @waypoint
- def compute_get_console_url(self, compute_id: int):
- api_response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/getConsoleUrl',
- arg_params={
- 'computeId': compute_id,
- },
- )
- return api_response.text
-
- @waypoint
- @checkmode
- def compute_migrate_to_zone(self, compute_id: int, zone_id: int):
- api_response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/migrateToZone',
- arg_params={
- 'computeId': compute_id,
- 'zoneId': zone_id,
- },
- )
- self.set_changed()
- return api_response.json()
-
@waypoint
def compute_get_snapshot_merge_status(self, vm_id: int) -> dict:
api_response = self.decort_api_call(
@@ -2460,40 +2501,6 @@ class DecortController(object):
)
return api_response.json()
- @waypoint
- @checkmode
- def compute_guest_agent_disable(self, vm_id: int):
- """
- Implementation of functionality of the API method
- `/cloudapi/compute/guest_agent_disable`.
- """
- response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/guest_agent_disable',
- arg_params={
- 'compute_id': vm_id,
- }
- )
- self.set_changed()
- return response.json()
-
- @waypoint
- @checkmode
- def compute_guest_agent_enable(self, vm_id: int):
- """
- Implementation of functionality of the API method
- `/cloudapi/compute/guest_agent_enable`.
- """
- response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/guest_agent_enable',
- arg_params={
- 'compute_id': vm_id,
- }
- )
- self.set_changed()
- return response.json()
-
@waypoint
def compute_guest_agent_feature_get(self, vm_id: int) -> list:
"""
@@ -2509,23 +2516,6 @@ class DecortController(object):
)
return response.json()
- @waypoint
- @checkmode
- def compute_guest_agent_feature_update(self, vm_id: int):
- """
- Implementation of functionality of the API method
- `/cloudapi/compute/guest_agent_feature_update`.
- """
- response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/guest_agent_feature_update', # noqa: E501
- arg_params={
- 'compute_id': vm_id,
- }
- )
- self.set_changed()
- return response.json()
-
@waypoint
@checkmode
def compute_guest_agent_execute(
@@ -2695,24 +2685,42 @@ class DecortController(object):
###################################
# OS image manipulation methods
###################################
- def _image_get_by_id(self, image_id):
- # TODO: update once cloudapi/image/get is implemented, see ticket #2963
+ def _image_get_by_id(self, image_id, fail_if_not_found=True):
+ try:
+ image_model = self.api.ca.image.get(
+ image_id=image_id,
+ show_all=True,
+ )
+ return image_model.id, image_model.model_dump()
+ except sdk_exceptions.RequestException as e:
+ if (
+ e.orig_exception.response is not None
+ and e.orig_exception.response.status_code == 404
+ ):
+ if fail_if_not_found:
+ self.message(
+ self.MESSAGES.obj_not_found(
+ obj='image',
+ id=image_id,
+ )
+ )
+ self.exit(fail=True)
+ else:
+ return 0, None
+ else:
+ raise e
- api_params = dict(imageId=image_id,
- showAll=True)
- api_resp = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/image/get',
- arg_params=api_params,
- not_fail_codes=[404],
- )
- if api_resp.status_code == 404:
- self.message(f'Image with ID {image_id} not found.')
- self.exit(fail=True)
- return image_id, api_resp.json()
-
- def image_find(self, image_id, image_name='', account_id=0, rg_id=0, sepid=0, pool=""):
+ def image_find(
+ self,
+ image_id,
+ image_name='',
+ account_id=0,
+ rg_id=0,
+ sepid=0,
+ pool="",
+ fail_if_not_found=True,
+ ):
"""Locates image specified by name and returns its facts as dictionary.
Primary use of this function is to obtain the ID of the image identified by its name and,
optionally SEP ID and/or pool name. Also note that only images in status CREATED are
@@ -2737,7 +2745,10 @@ class DecortController(object):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "image_find")
if image_id > 0:
- return self._image_get_by_id(image_id)
+ return self._image_get_by_id(
+ image_id,
+ fail_if_not_found=fail_if_not_found,
+ )
else:
validated_acc_id = account_id
if account_id == 0:
@@ -2748,11 +2759,11 @@ class DecortController(object):
return 0, None
validated_acc_id = rg_model.account_id
- api_params = dict(accountId=validated_acc_id)
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/list", api_params)
- # On success the above call will return here. On error it will abort execution by calling fail_json.
- images_list = json.loads(api_resp.content.decode('utf8'))
- for image_record in images_list['data']:
+ images_list = self.paginate_api_call(
+ "/restmachine/cloudapi/image/list",
+ dict(accountId=validated_acc_id),
+ )
+ for image_record in images_list:
if image_record['name'] == image_name and image_record['status'] == "CREATED":
if sepid == 0 and pool == "":
# if no filtering by SEP ID or pool name is requested, return the first match
@@ -2802,8 +2813,8 @@ class DecortController(object):
if image_id > 0:
ret_image_id, ret_image_dict = self._image_get_by_id(image_id)
if (ret_image_id and
- (sepid == 0 or sepid == ret_image_dict['sepId']) and
- (pool == "" or pool == ret_image_dict['pool'])):
+ (sepid == 0 or sepid == ret_image_dict['sep_id']) and
+ (pool == "" or pool == ret_image_dict['sep_pool_name'])):
return ret_image_id, ret_image_dict
else:
validated_acc_id = account_id
@@ -2815,11 +2826,11 @@ class DecortController(object):
return 0, None
validated_acc_id = rg_model.account_id
- api_params = dict(accountId=validated_acc_id)
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/list", api_params)
- # On success the above call will return here. On error it will abort execution by calling fail_json.
- images_list = json.loads(api_resp.content.decode('utf8'))
- for image_record in images_list['data']:
+ images_list = self.paginate_api_call(
+ "/restmachine/cloudapi/image/list",
+ dict(accountId=validated_acc_id),
+ )
+ for image_record in images_list:
if image_record['name'] == virt_name and image_record['status'] == "CREATED" and image_record['type'] == "virtual":
image_id, image_info = self._image_get_by_id(
image_id=image_record['id'],
@@ -2856,52 +2867,6 @@ class DecortController(object):
# On success the above call will return here. On error it will abort execution by calling fail_json.
virt_image_dict = json.loads(api_resp.content.decode('utf8'))
- self.result['failed'] = False
- self.result['changed'] = True
- return 0, None
-
- def image_create(
- self,
- img_name,
- url,
- username,
- password,
- account_id,
- usernameDL,
- passwordDL,
- sepId,
- poolName,
- storage_policy_id: int,
- boot_mode: Literal['bios', 'uefi'] = 'bios',
- boot_loader_type: Literal['linux', 'windows', 'unknown'] = 'unknown',
- network_interface_naming: Literal['eth', 'ens'] = 'ens',
- hot_resize: bool = False,
- ):
- self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "image_create")
-
- api_params = {
- 'name': img_name,
- 'url': url,
- 'boottype': boot_mode,
- 'imagetype': boot_loader_type,
- 'accountId': account_id,
- 'hotresize': hot_resize,
- 'username': username,
- 'password': password,
- 'usernameDL': usernameDL,
- 'passwordDL': passwordDL,
- 'sepId': sepId,
- 'poolName': poolName,
- 'networkInterfaceNaming': network_interface_naming,
- 'storage_policy_id': storage_policy_id,
- }
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/create", api_params)
- # On success the above call will return here. On error it will abort execution by calling fail_json.
- virt_image_dict = json.loads(api_resp.content.decode('utf8'))
- self.result['failed'] = False
- self.result['changed'] = True
- return 0, None
-
def virt_image_link(self, imageId, targetId):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "virt_image_link")
@@ -2916,29 +2881,6 @@ class DecortController(object):
return 0, None
- @waypoint
- @checkmode
- def image_change_storage_policy(
- self,
- image_id: int,
- storage_policy_id: int,
- ):
- """
- Implementation of functionality of the API method
- `/cloudapi/image/change_storage_policy`.
- """
- api_params = {
- 'image_id': image_id,
- 'storage_policy_id': storage_policy_id,
- }
- response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/image/change_storage_policy', # noqa: E501
- arg_params=api_params,
- )
- self.set_changed()
- return response.json()
-
###################################
# Resource Group (RG) manipulation methods
###################################
@@ -3021,11 +2963,9 @@ class DecortController(object):
rg_model: sdk_types.CloudapiRgGetResultModel | None = None
if arg_rg_id is not None and arg_rg_id > 0:
- rg_list = (
- self.api.cloudapi.rg.list(
- include_deleted=True,
- ).data
- )
+ rg_list = self.paginate_sdk_call(
+ self.api.cloudapi.rg.list,
+ )(include_deleted=True)
for rg_item in rg_list:
if rg_item.id == arg_rg_id:
got_id, got_specs = self._rg_get_by_id(rg_item.id)
@@ -3055,11 +2995,11 @@ class DecortController(object):
self.amodule.fail_json(**self.result)
# try to locate RG by name - start with getting all RGs IDs within the specified account
#api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/account/listRG", api_params)
- rg_list = (
- self.api.cloudapi.rg.list(
- include_deleted=True,
- account_id=arg_account_id
- ).data
+ rg_list = self.paginate_sdk_call(
+ self.api.cloudapi.rg.list,
+ )(
+ include_deleted=True,
+ account_id=arg_account_id,
)
for rg_item in rg_list:
if rg_item.name == arg_rg_name:
@@ -3096,6 +3036,7 @@ class DecortController(object):
arg_quota={},
location="",
sdn_access_group_id: None | str = None,
+ enable_default_gateway: bool = True,
):
"""Provision new RG according to the specified arguments.
If critical error occurs the embedded call to API function will abort further execution of the script
@@ -3139,6 +3080,7 @@ class DecortController(object):
extNetId=arg_extNetId,
extIp=arg_extIp,
sdn_access_group_id=sdn_access_group_id,
+ enable_default_gateway=enable_default_gateway,
)
if arg_quota:
if 'ram' in arg_quota:
@@ -3355,21 +3297,17 @@ class DecortController(object):
_account_id = account_id
if account_name and not account_id:
- accounts_list = (
- self.api.cloudapi.account.list(
- name=account_name
- ).data
- )
+ accounts_list = self.paginate_sdk_call(
+ self.api.cloudapi.account.list,
+ )(name=account_name)
for account in accounts_list:
if account.name == account_name:
_account_id = account.id
break
else:
- deleted_accounts_list = (
- self.api.cloudapi.account.list_deleted(
- name=account_name
- ).data
- )
+ deleted_accounts_list = self.paginate_sdk_call(
+ self.api.cloudapi.account.list_deleted,
+ )(name=account_name)
for account in deleted_accounts_list:
if account.name == account_name:
_account_id = account.id
@@ -3931,6 +3869,7 @@ class DecortController(object):
vins_name: str,
account_id: int,
security_group_mode: bool,
+ enable_default_gateway: bool,
rg_id: int | None = None,
ipcidr: str | None = None,
ext_net_id: int = -1,
@@ -3984,6 +3923,7 @@ class DecortController(object):
ip_cidr=ipcidr,
zone_id=zone_id,
security_group_mode=security_group_mode,
+ enable_default_gateway=enable_default_gateway,
)
elif rg_id:
ret_vins_id = self.sdk_checkmode(
@@ -3997,6 +3937,7 @@ class DecortController(object):
ip_cidr=ipcidr,
zone_id=zone_id,
security_group_mode=security_group_mode,
+ enable_default_gateway=enable_default_gateway,
)
else:
self.result['failed'] = True
@@ -4249,20 +4190,6 @@ class DecortController(object):
)
return
- @waypoint
- @checkmode
- def vins_migrate_to_zone(self, net_id: int, zone_id: int):
- api_response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/vins/migrateToZone',
- arg_params={
- 'net_id': net_id,
- 'zone_id': zone_id,
- },
- )
- self.set_changed()
- return api_response.json()
-
def _vnf_iface_add(self,arg_devid,arg_vxlanid,arg_ipaddr,arg_netmask="24",arg_defgw=""):
api_params = dict(
devId=arg_devid,
@@ -4502,7 +4429,12 @@ class DecortController(object):
return 0, None
elif name:
if account_id:
- disk_model_list = self.api.cloudapi.disks.list(account_id=account_id, name=name).data
+ disk_model_list = self.paginate_sdk_call(
+ self.api.cloudapi.disks.list,
+ )(
+ account_id=account_id,
+ name=name,
+ )
excluded_statuses = (sdk_types.DiskStatus.PURGED, sdk_types.DiskStatus.DESTROYED)
filter_f = lambda x: x.status not in excluded_statuses
disks_list = [d for d in disk_model_list if filter_f(d)]
@@ -4779,15 +4711,12 @@ class DecortController(object):
ret_k8s_id = 0
ret_k8s_dict = None
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/list", api_params)
-
- if api_resp.status_code == 200:
- k8s_list = json.loads(api_resp.content.decode('utf8'))
-
- if k8s_list['entryCount'] == 0:
- return None,None
-
- for k8s_item in k8s_list['data']:
+ k8s_list = self.paginate_api_call("/restmachine/cloudapi/k8s/list", api_params)
+
+ if not k8s_list:
+ return None, None
+
+ for k8s_item in k8s_list:
if not check_state or k8s_item['status'] not in K8S_INVALID_STATES:
ret_k8s_id = k8s_item['id']
ret_k8s_dict = self.k8s_get_by_id(ret_k8s_id)
@@ -5255,27 +5184,22 @@ class DecortController(object):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_k8ci_find")
- api_params = dict(includeDisabled=False)
+ k8ci_list = self.paginate_api_call(
+ "/restmachine/cloudapi/k8ci/list",
+ dict(includeDisabled=False),
+ )
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8ci/list", api_params)
k8ci_id_present = False
- if api_resp.status_code == 200:
- ret_k8ci_list = json.loads(api_resp.content.decode('utf8'))
- for k8ci_item in ret_k8ci_list['data']:
- if k8ci_item['id'] == arg_k8ci_id:
- k8ci_id_present = True
- break
-
- else:
- self.result['failed'] = True
- self.result['msg'] = ("Cannot find k8ci id: {}.").format(arg_k8ci_id)
- self.amodule.fail_json(**self.result)
+ for k8ci_item in k8ci_list:
+ if k8ci_item['id'] == arg_k8ci_id:
+ k8ci_id_present = True
+ break
else:
self.result['failed'] = True
- self.result['msg'] = ("Failed to get k8ci list HTTP code {}.").format(api_resp.status_code)
+ self.result['msg'] = ("Cannot find k8ci id: {}.").format(arg_k8ci_id)
self.amodule.fail_json(**self.result)
return arg_k8ci_id
-
+
def k8s_getConfig(self):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_getConfig")
@@ -5285,20 +5209,6 @@ class DecortController(object):
ret_conf = api_resp.content.decode('utf8')
return ret_conf
- @waypoint
- @checkmode
- def k8s_migrate_to_zone(self, k8s_id: int, zone_id: int):
- api_response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/k8s/migrateToZone',
- arg_params={
- 'k8sId': k8s_id,
- 'zoneId': zone_id,
- },
- )
- self.set_changed()
- return api_response.json()
-
def k8s_get_master_node_storage_policy_id(self, k8s_info: dict) -> int:
master = k8s_info['node_groups']['master']
master_nodes_info = master.get('vms') or master.get('detailedInfo', [])
@@ -5316,41 +5226,54 @@ class DecortController(object):
# Bservice management
#
##############################
- def bservice_get_by_id(self,bs_id):
+ def bservice_get_by_id(
+ self,
+ bs_id,
+ fail_if_not_found=True,
+ ):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "bservice_get_by_id")
- ret_bs_id = 0
- ret_bs_dict = dict()
-
if not bs_id:
self.result['failed'] = True
self.result['msg'] = "bservice_get_by_id(): zero B-Service ID specified."
self.amodule.fail_json(**self.result)
- api_params = dict(serviceId=bs_id, )
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/bservice/get", api_params)
- if api_resp.status_code == 200:
- ret_bs_id = bs_id
- ret_bs_dict = json.loads(api_resp.content.decode('utf8'))
- else:
- self.result['warning'] = ("bservice_get_by_id(): failed to get B-service by ID {}. HTTP code {}, "
- "response {}.").format(bs_id, api_resp.status_code, api_resp.reason)
+ try:
+ bs_model = self.api.ca.bservice.get(bservice_id=bs_id)
+ return bs_model.model_dump()
+ except sdk_exceptions.RequestException as e:
+ if (
+ e.orig_exception.response is not None
+ and e.orig_exception.response.status_code == 404
+ ):
+ if fail_if_not_found:
+ self.message(
+ self.MESSAGES.obj_not_found(
+ obj='bservice',
+ id=bs_id,
+ )
+ )
+ self.exit(fail=True)
+ else:
+ return None
+ else:
+ raise e
- return ret_bs_id, ret_bs_dict
def _bservice_rg_list(self,acc_id,rg_id):
- ret_bs_dict=dict()
- api_params = dict(accountId=acc_id,rgId=rg_id )
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/bservice/list", api_params)
- if api_resp.status_code == 200:
- ret_bs_dict = json.loads(api_resp.content.decode('utf8'))
- else:
- self.result['warning'] = ("bservice_rg_list(): failed to get B-service list. HTTP code {}, "
- "response {}.").format(api_resp.status_code, api_resp.reason)
- return []
- return ret_bs_dict['data']
+ return self.paginate_api_call(
+ "/restmachine/cloudapi/bservice/list",
+ dict(accountId=acc_id, rgId=rg_id),
+ )
- def bservice_find(self,account_id,rg_id,bservice_name="",bservice_id = 0,check_state=True):
+ def bservice_find(
+ self,
+ account_id,
+ rg_id,
+ bservice_name="",
+ bservice_id = 0,
+ fail_if_not_found=True,
+ ):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "bservice_find")
@@ -5361,8 +5284,15 @@ class DecortController(object):
bservice_id = int(srv['id'])
if bservice_id > 0:
- ret_bs_id,ret_bs_dict = self.bservice_get_by_id(bservice_id)
- return ret_bs_id,ret_bs_dict
+ ret_bs_dict = self.bservice_get_by_id(
+ bs_id=bservice_id,
+ fail_if_not_found=fail_if_not_found,
+ )
+ if ret_bs_dict:
+ ret_bs_id = ret_bs_dict['id']
+ return ret_bs_id, ret_bs_dict
+ else:
+ return 0, None
else:
return bservice_id,None
@@ -5398,12 +5328,12 @@ class DecortController(object):
if desired_state == 'disabled':
sdk_func = self.api.ca.bservice.disable
elif (
- bs_dict['techStatus'] == 'STARTED'
+ bs_dict['tech_status'] == 'STARTED'
and desired_state == 'stopped'
):
sdk_func = self.api.ca.bservice.stop
elif (
- bs_dict['techStatus'] == 'STOPPED'
+ bs_dict['tech_status'] == 'STOPPED'
and desired_state == 'started'
):
sdk_func = self.api.ca.bservice.start
@@ -5425,20 +5355,6 @@ class DecortController(object):
return
- @waypoint
- @checkmode
- def bservice_migrate_to_zone(self, bs_id: int, zone_id: int):
- api_response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/bservice/migrateToZone',
- arg_params={
- 'serviceId': bs_id,
- 'zoneId': zone_id,
- },
- )
- self.set_changed()
- return api_response.json()
-
#
# GROUP MANAGE
#
@@ -5458,7 +5374,7 @@ class DecortController(object):
f'Group with ID {g_id} for BService with ID {bs_id} not found '
f'or Group with ID {g_id} wast deleted.'
)
- self.exit()
+ self.exit(fail=True)
else:
self.result['warning'] = (
f'group_get_by_id(): failed to get Group by ID {g_id}. '
@@ -5483,24 +5399,18 @@ class DecortController(object):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "group_state")
- group_api=""
+ sdk_func = None
if desired_state == 'stopped':
- group_api = "/restmachine/cloudapi/bservice/groupStop"
- state_expected = "STOPPED"
+ sdk_func = self.api.ca.bservice.group_stop
else:
- group_api = "/restmachine/cloudapi/bservice/groupStart"
- state_expected = "STARTED"
- api_params = dict(
- serviceId=bs_id,
- compgroupId=gr_id
- )
- if group_api != "":
- self.decort_api_call(requests.post, group_api, api_params)
- # On success the above call will return here. On error it will abort execution by calling fail_json.
- self.result['failed'] = False
- self.result['changed'] = True
+ sdk_func = self.api.ca.bservice.group_start
+
+ if sdk_func is not None:
+ self.sdk_checkmode(sdk_func)(
+ bservice_id=bs_id,
+ bsgroup_id=gr_id,
+ )
else:
- self.result['failed'] = False
self.result['msg'] = ("group_state(): no start/stop action required for B-service ID {} "
"to desired state '{}'.").format(bs_id,desired_state)
return
@@ -5532,27 +5442,6 @@ class DecortController(object):
self.result['msg'] = ("group_resize_count(): no need resize Group ID {}.").format(gr_dict['id'])
return
- @waypoint
- def group_update(self,bs_id,gr_dict,arg_cpu,arg_disk,arg_name,arg_role,arg_ram):
- api_params=dict(
- serviceId=bs_id,
- compgroupId=gr_dict['id'],
- force=True,
- cpu=arg_cpu,
- ram=arg_ram,
- role=arg_role,
- disk=arg_disk,
- name=arg_name,
- )
-
- self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/bservice/groupUpdate',
- arg_params=api_params,
- )
-
- self.set_changed()
-
def group_update_net(self,bs_id,gr_dict,arg_net):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "group_update_net")
@@ -5564,28 +5453,23 @@ class DecortController(object):
list_vins.append(net['id'])
else:
list_extnet.append(net['id'])
- if len(list_vins) > 0:
+ if len(list_vins) > 0:
if sorted(gr_dict['vinses']) != sorted(list_vins):
- api_url = "/restmachine/cloudapi/bservice/groupUpdateVins"
- api_params = dict(
- serviceId=bs_id,
- compgroupId=gr_dict['id'],
- vinses=list_vins
+ self.sdk_checkmode(self.api.cloudapi.bservice.group_update_vins)(
+ bservice_id=bs_id,
+ bsgroup_id=gr_dict['id'],
+ vins_ids=list_vins,
)
- self.decort_api_call(requests.post, api_url, api_params)
- self.set_changed()
- if len(list_extnet) > 0:
+ if len(list_extnet) > 0:
if sorted(gr_dict['extnets']) != sorted(list_extnet):
- api_url = '/restmachine/cloudapi/bservice/groupUpdateExtnet'
- api_params = dict(
- serviceId=bs_id,
- compgroupId=gr_dict['id'],
- extnets=list_extnet
+ self.sdk_checkmode(self.api.cloudapi.bservice.group_update_extnet)(
+ bservice_id=bs_id,
+ bsgroup_id=gr_dict['id'],
+ extnet_ids=list_extnet,
)
- self.decort_api_call(requests.post, api_url, api_params)
- self.set_changed()
return
+
def group_provision(
self,
bs_id,
@@ -5627,21 +5511,7 @@ class DecortController(object):
self.result['failed'] = False
self.result['changed'] = True
return new_bsgroup_id
-
- def group_delete(self,bs_id,gr_id):
-
- self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "group_delete")
-
- api_url = "/restmachine/cloudapi/bservice/groupRemove"
- api_params=dict(
- serviceId = bs_id,
- compgroupId = gr_id
- )
- self.decort_api_call(requests.post, api_url, api_params)
- self.result['failed'] = False
- self.result['msg'] = "group_delete() Group ID {} was deleted.".format(gr_id)
- self.result['changed'] = True
- return
+
####################
### LB MANAGMENT ###
####################
@@ -5680,26 +5550,6 @@ class DecortController(object):
else:
raise e
- def _rg_listlb(self,rg_id):
- """List all LB in the resource group
- @param (int) rg_id: id onr resource group
- """
- if not rg_id:
- self.result['failed'] = True
- self.result['msg'] = "_rg_listlb(): zero RG ID specified."
- self.amodule.fail_json(**self.result)
-
- api_params = dict(includedeleted=True)
- api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/lb/list", api_params)
- if api_resp.status_code == 200:
- ret_rg_vins_list = json.loads(api_resp.content.decode('utf8'))
- else:
- self.result['warning'] = ("rg_listlb(): failed to get RG by ID {}. HTTP code {}, "
- "response {}.").format(rg_id, api_resp.status_code, api_resp.reason)
- return []
-
- return ret_rg_vins_list['data']
-
@handle_sdk_exceptions
def lb_find(self,lb_id=0,lb_name="",rg_id=0):
"""Find specified LB.
@@ -5738,7 +5588,11 @@ class DecortController(object):
return 0, None
elif lb_name != "":
if rg_id > 0:
- list_lb = self._rg_listlb(rg_id)
+ list_lb = [
+ lb.model_dump() for lb in self.paginate_sdk_call(
+ self.api.ca.lb.list,
+ )(include_deleted=True)
+ ]
for lb in list_lb:
if lb['name'] == lb_name:
ret_lb_model = self.api.cloudapi.lb.get(lb_id=lb['id'])
@@ -5783,36 +5637,26 @@ class DecortController(object):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "lb_provision")
- if self.amodule.check_mode:
- self.result['failed'] = False
- self.result['msg'] = ("vins_lb() in check mode: provision LB name '{}' was "
- "requested in RG with id: {}.").format(lb_name,rg_id)
- return 0
-
if lb_name == "":
self.result['failed'] = True
self.result['msg'] = "lb_provision(): LB name cannot be empty."
self.amodule.fail_json(**self.result)
- api_url = "/restmachine/cloudapi/lb/create"
- api_params = dict(
- name=lb_name,
- rgId=rg_id,
- extnetId=ext_net_id,
- vinsId=vins_id,
- highlyAvailable=ha_status,
- start=start,
- desc=description,
- sysctlParams=sysctl and json.dumps(
- {k: str(v) for k, v in sysctl.items()}
- ),
- zoneId=zone_id,
+ sysctl_params = None
+ if sysctl is not None:
+ sysctl_params = [{k: str(v) for k, v in sysctl.items()}]
+
+ ret_lb_id = self.sdk_checkmode(self.api.ca.lb.create)(
+ ext_net_id=ext_net_id,
+ name=lb_name,
+ rg_id=rg_id,
+ vins_id=vins_id,
+ description=description,
+ ha_mode=ha_status,
+ start=start,
+ sysctl_params=sysctl_params,
+ zone_id=zone_id,
)
- api_resp = self.decort_api_call(requests.post, api_url, api_params)
- # On success the above call will return here. On error it will abort execution by calling fail_json.
- self.result['failed'] = False
- self.result['changed'] = True
- ret_lb_id = int(api_resp.content.decode('utf8'))
return ret_lb_id
def lb_state(
@@ -6452,21 +6296,6 @@ class DecortController(object):
)
self.set_changed()
- @waypoint
- @checkmode
- def lb_migrate_to_zone(self, lb_id: int, zone_id: int):
- api_response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/lb/migrateToZone',
- arg_params={
- 'lbId': lb_id,
- 'zoneId': zone_id,
- },
- )
- self.set_changed()
- return api_response.json()
-
-
@waypoint
@checkmode
def snapshot_delete(
@@ -6565,23 +6394,6 @@ class DecortController(object):
snapshots_usage = snapshot_usage[1:]
return common_snapshot_usage_info, snapshots_usage
- @waypoint
- @checkmode
- def snapshot_abort_merge(self, vm_id: int, label: str) -> str:
- response = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/compute/abort_shared_snapshot_merge', # noqa: E501
- arg_params={
- 'compute_id': vm_id,
- 'label': label,
- },
- )
- self.set_changed()
- self.message(
- f'Merge aborted for snapshot {label} of VM ID {vm_id}.'
- )
- return response.text
-
def check_aparam_zone_id(self) -> bool | None:
aparam_zone_id = self.aparams['zone_id']
if aparam_zone_id is not None:
@@ -6659,27 +6471,3 @@ class DecortController(object):
self.exit(fail=True)
return extnet_info
-
- @waypoint
- @checkmode
- def security_group_detele_rule(
- self,
- security_group_id: int,
- rule_id: int,
- ) -> bool:
- """
- Implementation of functionality of the API method
- `/cloudapi/security_group/delete_rule`.
- """
-
- api_resp = self.decort_api_call(
- arg_req_function=requests.post,
- arg_api_name='/restmachine/cloudapi/security_group/delete_rule',
- arg_params={
- 'security_group_id': security_group_id,
- 'rule_id': rule_id,
- },
- )
- self.set_changed()
-
- return api_resp.json()
diff --git a/requirements.txt b/requirements.txt
index 3ba3f1b..66ecceb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
ansible==11.6.0
requests==2.32.3
-git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@1.5.latest
+git+https://repository.basistech.ru/BASIS/dynamix-python-sdk.git@2.0.latest