9.0.0
This commit is contained in:
@@ -4,7 +4,7 @@ DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_account
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from typing import Iterable
|
||||
@@ -114,6 +114,9 @@ class DecortAccount(DecortController):
|
||||
description=dict(
|
||||
type='str',
|
||||
),
|
||||
default_zone_id=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
required_one_of=[
|
||||
('id', 'name')
|
||||
@@ -164,15 +167,24 @@ class DecortAccount(DecortController):
|
||||
if check_error:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_error = False
|
||||
if self.check_aparam_default_zone_id() is False:
|
||||
check_error = True
|
||||
|
||||
if check_error:
|
||||
self.exit(fail=True)
|
||||
|
||||
def run(self):
|
||||
self.get_info()
|
||||
self.check_amodule_args_for_change()
|
||||
self.change()
|
||||
self.exit()
|
||||
|
||||
def get_info(self):
|
||||
# If this is the first getting info
|
||||
if not getattr(self, 'id', None):
|
||||
self.id, self.facts = self.account_find(
|
||||
if self._acc_info is None:
|
||||
self.acc_id, self._acc_info = self.account_find(
|
||||
account_name=self.aparams['name'],
|
||||
account_id=self.aparams['id'],
|
||||
)
|
||||
@@ -181,7 +193,10 @@ class DecortAccount(DecortController):
|
||||
# If check mode is enabled, there is no needed to
|
||||
# request info again
|
||||
if not self.amodule.check_mode:
|
||||
self.id, self.facts = self.account_find(account_id=self.id)
|
||||
self.acc_id, self._acc_info = self.account_find(
|
||||
account_id=self.acc_id,
|
||||
)
|
||||
self.facts = self.acc_info
|
||||
|
||||
def change(self):
|
||||
self.change_state()
|
||||
@@ -189,12 +204,12 @@ class DecortAccount(DecortController):
|
||||
self.change_acl()
|
||||
|
||||
if self.account_update_args:
|
||||
self.account_update(account_id=self.id,
|
||||
self.account_update(account_id=self.acc_id,
|
||||
**self.account_update_args)
|
||||
self.get_info()
|
||||
|
||||
def change_state(self):
|
||||
match self.facts:
|
||||
match self._acc_info:
|
||||
case None:
|
||||
self.message(self.MESSAGES.obj_not_found(obj=self.OBJ))
|
||||
match self.aparams:
|
||||
@@ -208,7 +223,7 @@ class DecortAccount(DecortController):
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj=self.OBJ,
|
||||
id=self.id,
|
||||
id=self.acc_id,
|
||||
permanently=True,
|
||||
already=True,
|
||||
)
|
||||
@@ -216,7 +231,7 @@ class DecortAccount(DecortController):
|
||||
case {'state': 'confirmed' | 'disabled' | 'present'}:
|
||||
self.message(
|
||||
self.MESSAGES.obj_not_restored(obj=self.OBJ,
|
||||
id=self.id)
|
||||
id=self.acc_id)
|
||||
)
|
||||
self.exit(fail=True)
|
||||
case {'status': 'DELETED'}:
|
||||
@@ -225,7 +240,7 @@ class DecortAccount(DecortController):
|
||||
self.message(
|
||||
self.MESSAGES.obj_deleted(
|
||||
obj=self.OBJ,
|
||||
id=self.id,
|
||||
id=self.acc_id,
|
||||
permanently=False,
|
||||
already=True,
|
||||
)
|
||||
@@ -259,26 +274,28 @@ class DecortAccount(DecortController):
|
||||
pass
|
||||
|
||||
def delete(self, permanently=False):
|
||||
self.account_delete(account_id=self.id, permanently=permanently)
|
||||
self.account_delete(account_id=self.acc_id, permanently=permanently)
|
||||
self.get_info()
|
||||
|
||||
def disable(self):
|
||||
self.account_disable(account_id=self.id)
|
||||
self.account_disable(account_id=self.acc_id)
|
||||
self.get_info()
|
||||
|
||||
def enable(self):
|
||||
self.account_enable(account_id=self.id)
|
||||
self.account_enable(account_id=self.acc_id)
|
||||
self.get_info()
|
||||
|
||||
def restore(self):
|
||||
self.account_restore(account_id=self.id)
|
||||
self.account_restore(account_id=self.acc_id)
|
||||
self.get_info()
|
||||
|
||||
def change_acl(self):
|
||||
if not self.aparams['acl']:
|
||||
return
|
||||
|
||||
actual_users = {u['userGroupId']: u['right'] for u in self.facts['acl']}
|
||||
actual_users = {
|
||||
u['userGroupId']: u['right'] for u in self.acc_info['acl']
|
||||
}
|
||||
actual_users_ids = set(actual_users.keys())
|
||||
|
||||
aparams_acl = self.aparams['acl']
|
||||
@@ -291,9 +308,13 @@ class DecortAccount(DecortController):
|
||||
|
||||
match aparams_acl:
|
||||
case {'mode': 'revoke'}:
|
||||
del_users_ids = aparams_users_ids.intersection(actual_users_ids)
|
||||
del_users_ids = aparams_users_ids.intersection(
|
||||
actual_users_ids,
|
||||
)
|
||||
case {'mode': 'update' | 'match' as mode}:
|
||||
new_users_ids = aparams_users_ids.difference(actual_users_ids)
|
||||
new_users_ids = aparams_users_ids.difference(
|
||||
actual_users_ids,
|
||||
)
|
||||
new_users = dict(
|
||||
u for u in aparams_users.items() if u[0] in new_users_ids
|
||||
)
|
||||
@@ -315,7 +336,7 @@ class DecortAccount(DecortController):
|
||||
actual_users_ids.difference(aparams_users_ids)
|
||||
|
||||
if del_users_ids or new_users or upd_users:
|
||||
self.account_change_acl(account_id=self.id,
|
||||
self.account_change_acl(account_id=self.acc_id,
|
||||
del_users=del_users_ids,
|
||||
add_users=new_users,
|
||||
upd_users=upd_users)
|
||||
@@ -327,12 +348,12 @@ class DecortAccount(DecortController):
|
||||
|
||||
aparam_access_emails = self.aparams['access_emails']
|
||||
if (aparam_access_emails is not None
|
||||
and self.facts['sendAccessEmails'] != aparam_access_emails):
|
||||
and self.acc_info['sendAccessEmails'] != aparam_access_emails):
|
||||
result_args['access_emails'] = aparam_access_emails
|
||||
|
||||
aparam_name = self.aparams['name']
|
||||
if (self.aparams['id'] and aparam_name
|
||||
and self.facts['name'] != aparam_name):
|
||||
and self.acc_info['name'] != aparam_name):
|
||||
result_args['name'] = aparam_name
|
||||
|
||||
aparam_quotas = self.aparams['quotas']
|
||||
@@ -346,7 +367,7 @@ class DecortAccount(DecortController):
|
||||
['ram', 'CU_M', 'ram_quota'],
|
||||
]
|
||||
for aparam, info_key, result_arg in quotas_naming:
|
||||
current_value = int(self.facts['resourceLimits'][info_key])
|
||||
current_value = int(self.acc_info['resourceLimits'][info_key])
|
||||
if (aparam_quotas[aparam] is not None
|
||||
and current_value != aparam_quotas[aparam]):
|
||||
result_args[result_arg] = aparam_quotas[aparam]
|
||||
@@ -359,18 +380,38 @@ class DecortAccount(DecortController):
|
||||
sep_pools.add(
|
||||
f'{sep["sep_id"]}_{pool_name}'
|
||||
)
|
||||
if set(self.facts['uniqPools']) != sep_pools:
|
||||
if set(self.acc_info['uniqPools']) != sep_pools:
|
||||
result_args['sep_pools'] = sep_pools
|
||||
|
||||
aparam_desc = self.aparams['description']
|
||||
if (
|
||||
aparam_desc is not None
|
||||
and self.facts['description'] != aparam_desc
|
||||
and self.acc_info['description'] != aparam_desc
|
||||
):
|
||||
result_args['description'] = aparam_desc
|
||||
|
||||
aparam_default_zone_id = self.aparams['default_zone_id']
|
||||
if (
|
||||
aparam_default_zone_id is not None
|
||||
and self.acc_info['defaultZoneId'] != aparam_default_zone_id
|
||||
):
|
||||
result_args['default_zone_id'] = aparam_default_zone_id
|
||||
|
||||
return result_args
|
||||
|
||||
def check_aparam_default_zone_id(self) -> bool | None:
|
||||
aparam_default_zone_id = self.aparams['default_zone_id']
|
||||
if aparam_default_zone_id is not None:
|
||||
if aparam_default_zone_id in self.acc_zone_ids:
|
||||
return True
|
||||
else:
|
||||
self.message(
|
||||
'Check for parameter "default_zone_id" failed: '
|
||||
f'zone ID {aparam_default_zone_id} not available '
|
||||
f'for account ID {self.acc_id}.'
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
DecortAccount().run()
|
||||
|
||||
@@ -4,7 +4,7 @@ DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_account_info
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
@@ -78,8 +78,7 @@ class DecortAccountInfo(DecortController):
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=\
|
||||
self.FIELDS_FOR_SORTING_ACCOUNT_COMPUTE_LIST,
|
||||
choices=self.FIELDS_FOR_SORTING_ACCOUNT_COMPUTE_LIST, # noqa: E501
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
@@ -129,8 +128,7 @@ class DecortAccountInfo(DecortController):
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=\
|
||||
self.FIELDS_FOR_SORTING_ACCOUNT_DISK_LIST,
|
||||
choices=self.FIELDS_FOR_SORTING_ACCOUNT_DISK_LIST, # noqa: E501
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
@@ -221,8 +219,7 @@ class DecortAccountInfo(DecortController):
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=\
|
||||
self.FIELDS_FOR_SORTING_ACCOUNT_IMAGE_LIST,
|
||||
choices=self.FIELDS_FOR_SORTING_ACCOUNT_IMAGE_LIST, # noqa: E501
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
@@ -278,8 +275,7 @@ class DecortAccountInfo(DecortController):
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=\
|
||||
self.FIELDS_FOR_SORTING_ACCOUNT_RG_LIST,
|
||||
choices=self.FIELDS_FOR_SORTING_ACCOUNT_RG_LIST, # noqa: E501
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
@@ -332,8 +328,7 @@ class DecortAccountInfo(DecortController):
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=\
|
||||
self.FIELDS_FOR_SORTING_ACCOUNT_VINS_LIST,
|
||||
choices=self.FIELDS_FOR_SORTING_ACCOUNT_VINS_LIST, # noqa: E501
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -20,6 +20,7 @@ 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
|
||||
@@ -27,7 +28,7 @@ class decort_bservice(DecortController):
|
||||
self.fail_json(**self.result)
|
||||
if not arg_amodule.params['id']:
|
||||
if not arg_amodule.params['rg_id']: # RG ID is not set -> locate RG by name -> need account ID
|
||||
validated_acc_id, _ = self.account_find(arg_amodule.params['account_name'],
|
||||
validated_acc_id, self.acc_info = self.account_find(arg_amodule.params['account_name'],
|
||||
arg_amodule.params['account_id'])
|
||||
if not validated_acc_id:
|
||||
self.result['failed'] = True
|
||||
@@ -53,7 +54,7 @@ class decort_bservice(DecortController):
|
||||
arg_amodule.params['rg_id'] = validated_rg_id
|
||||
arg_amodule.params['rg_name'] = validated_rg_facts['name']
|
||||
validated_acc_id = validated_rg_facts['accountId']
|
||||
|
||||
|
||||
self.bservice_id, self.bservice_info = self.bservice_find(
|
||||
validated_acc_id,
|
||||
validated_rg_id,
|
||||
@@ -61,10 +62,14 @@ class decort_bservice(DecortController):
|
||||
arg_amodule.params['id']
|
||||
)
|
||||
|
||||
if self.bservice_id == 0:
|
||||
self.bservice_should_exist = False
|
||||
else:
|
||||
self.acc_id = validated_acc_id or self.bservice_info['accountId']
|
||||
|
||||
if self.bservice_id and self.bservice_info['status'] != 'DESTROYED':
|
||||
self.bservice_should_exist = True
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
self.bservice_should_exist = False
|
||||
self.check_amodule_args_for_create()
|
||||
|
||||
def nop(self):
|
||||
"""No operation (NOP) handler for B-service.
|
||||
@@ -103,15 +108,23 @@ class decort_bservice(DecortController):
|
||||
self.amodule.params['name'],
|
||||
self.amodule.params['rg_id'],
|
||||
self.amodule.params['sshuser'],
|
||||
self.amodule.params['sshkey']
|
||||
self.amodule.params['sshkey'],
|
||||
zone_id=self.aparams['zone_id'],
|
||||
)
|
||||
if self.bservice_id:
|
||||
_, self.bservice_info = self.bservice_get_by_id(self.bservice_id)
|
||||
self.bservice_state(self.bservice_info,'enabled',self.amodule.params['started'])
|
||||
self.bservice_state(self.bservice_info,'enabled')
|
||||
return
|
||||
|
||||
def action(self,d_state,started=False):
|
||||
self.bservice_state(self.bservice_info,d_state,started)
|
||||
def action(self,d_state):
|
||||
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,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
return
|
||||
|
||||
def restore(self):
|
||||
@@ -147,6 +160,7 @@ class decort_bservice(DecortController):
|
||||
ret_dict['rg_id'] = self.bservice_info['rgId']
|
||||
ret_dict['account_id'] = self.bservice_info['accountId']
|
||||
ret_dict['groups'] = self.bservice_info['groups']
|
||||
ret_dict['zone_id'] = self.bservice_info['zoneId']
|
||||
return ret_dict
|
||||
|
||||
@property
|
||||
@@ -168,13 +182,10 @@ class decort_bservice(DecortController):
|
||||
'disabled',
|
||||
'enabled',
|
||||
'present',
|
||||
'check',
|
||||
'started',
|
||||
'stopped',
|
||||
],
|
||||
),
|
||||
started=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
name=dict(
|
||||
type='str',
|
||||
default='',
|
||||
@@ -197,6 +208,9 @@ class decort_bservice(DecortController):
|
||||
type='str',
|
||||
default='',
|
||||
),
|
||||
zone_id=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
@@ -204,12 +218,54 @@ class decort_bservice(DecortController):
|
||||
('rg_id', 'rg_name'),
|
||||
],
|
||||
)
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
|
||||
self.is_bservice_stopped_or_will_be_stopped = (
|
||||
(
|
||||
self.bservice_info['techStatus'] == 'STOPPED'
|
||||
and (
|
||||
self.aparams['state'] is None
|
||||
or self.aparams['state'] in ('present', 'stopped')
|
||||
)
|
||||
)
|
||||
or (
|
||||
self.bservice_info['techStatus'] != 'STOPPED'
|
||||
and self.aparams['state'] == 'stopped'
|
||||
)
|
||||
)
|
||||
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
if (
|
||||
self.aparams['zone_id'] is not None
|
||||
and self.aparams['zone_id'] != self.bservice_info['zoneId']
|
||||
and not self.is_bservice_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "zone_id" failed: '
|
||||
'Basic Service must be stopped to migrate to a zone.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
|
||||
def main():
|
||||
subj = decort_bservice()
|
||||
amodule = subj.amodule
|
||||
|
||||
if amodule.params['state'] == 'check':
|
||||
if subj.amodule.check_mode:
|
||||
subj.result['changed'] = False
|
||||
if subj.bservice_id:
|
||||
subj.result['failed'] = False
|
||||
@@ -235,30 +291,23 @@ def main():
|
||||
"ENABLING","DISABLING","RESTORING","MODELED"):
|
||||
subj.error()
|
||||
elif subj.bservice_info['status'] == "DELETED":
|
||||
if amodule.params['state'] in ('disabled', 'enabled', 'present'):
|
||||
if amodule.params['state'] in (
|
||||
'disabled', 'enabled', 'present', 'started', 'stopped'
|
||||
):
|
||||
subj.restore(subj.bservice_id)
|
||||
subj.action(amodule.params['state'],amodule.params['started'])
|
||||
subj.action(amodule.params['state'])
|
||||
if amodule.params['state'] == 'absent':
|
||||
subj.nop()
|
||||
elif subj.bservice_info['techStatus'] in ("STARTED","STOPPED"):
|
||||
if amodule.params['state'] == 'disabled':
|
||||
subj.action(amodule.params['state'],amodule.params['started'])
|
||||
elif amodule.params['state'] == 'absent':
|
||||
subj.destroy()
|
||||
else:
|
||||
subj.action(amodule.params['state'],amodule.params['started'])
|
||||
elif subj.bservice_info['status'] == "DISABLED":
|
||||
elif subj.bservice_info['status'] in ('ENABLED', 'DISABLED'):
|
||||
if amodule.params['state'] == 'absent':
|
||||
subj.destroy()
|
||||
elif amodule.params['state'] in ('present','enabled'):
|
||||
subj.action(amodule.params['state'],amodule.params['started'])
|
||||
else:
|
||||
subj.nop()
|
||||
subj.action(amodule.params['state'])
|
||||
elif subj.bservice_info['status'] == "DESTROED":
|
||||
if amodule.params['state'] in ('present','enabled'):
|
||||
subj.create()
|
||||
subj.action(amodule.params['state'],amodule.params['started'])
|
||||
if amodule.params['state'] == 'absent':
|
||||
subj.action(amodule.params['state'])
|
||||
if amodule.params['state'] == 'absent':
|
||||
subj.nop()
|
||||
else:
|
||||
if amodule.params['state'] == 'absent':
|
||||
@@ -272,9 +321,10 @@ def main():
|
||||
amodule.fail_json(**subj.result)
|
||||
else:
|
||||
if subj.bservice_should_exist:
|
||||
_, subj.bservice_info = subj.bservice_get_by_id(subj.bservice_id)
|
||||
subj.result['facts'] = subj.package_facts(amodule.check_mode)
|
||||
amodule.exit_json(**subj.result)
|
||||
else:
|
||||
amodule.exit_json(**subj.result)
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -52,7 +52,7 @@ class decort_disk(DecortController):
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
self.acc_id = validated_acc_id
|
||||
self.acc_info = validated_acc_info
|
||||
self._acc_info = validated_acc_info
|
||||
validated_disk_id, validated_disk_facts = self.disk_find(
|
||||
disk_id=arg_amodule.params['id'],
|
||||
name=arg_amodule.params['name'] if "name" in arg_amodule.params else "",
|
||||
@@ -192,7 +192,6 @@ class decort_disk(DecortController):
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
default='Disk by decort_disk',
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
@@ -348,4 +347,4 @@ def main():
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
#SHARE
|
||||
#SHARE
|
||||
|
||||
@@ -389,4 +389,4 @@ def main():
|
||||
amodule.exit_json(**subj.result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -4,7 +4,7 @@ DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_jwt
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
@@ -23,6 +23,7 @@ class decort_k8s(DecortController):
|
||||
validated_rg_facts = None
|
||||
validated_k8ci_id = 0
|
||||
self.k8s_should_exist = False
|
||||
self.is_k8s_stopped_or_will_be_stopped: None | bool = None
|
||||
|
||||
self.wg_default_params = {
|
||||
'num': 1,
|
||||
@@ -77,62 +78,12 @@ class decort_k8s(DecortController):
|
||||
rg_id=validated_rg_id,
|
||||
check_state=False)
|
||||
|
||||
if self.k8s_id:
|
||||
if self.k8s_id and self.k8s_info['status'] != 'DESTROYED':
|
||||
self.k8s_should_exist = True
|
||||
self.acc_id = self.k8s_info['accountId']
|
||||
# check workers and groups for add or remove?
|
||||
|
||||
aparam_sysctl = arg_amodule.params['lb_sysctl']
|
||||
if aparam_sysctl is not None:
|
||||
_, lb_info = self._lb_get_by_id(lb_id=self.k8s_info['lbId'])
|
||||
sysctl_with_str_values = {
|
||||
k: str(v) for k, v in aparam_sysctl.items()
|
||||
}
|
||||
if sysctl_with_str_values != lb_info['sysctlParams']:
|
||||
self.message(
|
||||
'Check for parameter "lb_sysctl" failed: '
|
||||
'cannot change lb_sysctl for an existing cluster '
|
||||
'load balancer.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
if not self.k8s_id:
|
||||
validated_k8ci_id = self.k8s_k8ci_find(arg_amodule.params['k8ci_id'])
|
||||
if not validated_k8ci_id:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = "Cannot find K8CI ID {}.".format(arg_amodule.params['k8ci_id'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
if not arg_amodule.params['workers']:
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = "At least one worker group must be present"
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
if (
|
||||
arg_amodule.params['lb_sysctl'] is not None
|
||||
and not arg_amodule.params['with_lb']
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "lb_sysctl" failed: '
|
||||
'"lb_sysctl" can only be set if the parameter "with_lb" '
|
||||
'is set to True.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
if (
|
||||
self.aparams['master_count'] is not None
|
||||
and self.aparams['master_count'] > 1
|
||||
and not self.aparams['with_lb']
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "master_count" failed: '
|
||||
'master_count can be more than 1 only if the parameter '
|
||||
'"with_lb" is set to True.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
return
|
||||
|
||||
def package_facts(self,check_mode=False):
|
||||
@@ -168,6 +119,7 @@ class decort_k8s(DecortController):
|
||||
ret_dict['k8s_Workers'] = self.k8s_info['k8sGroups']['workers']
|
||||
ret_dict['lb_id'] = self.k8s_info['lbId']
|
||||
ret_dict['description'] = self.k8s_info['desc']
|
||||
ret_dict['zone_id'] = self.k8s_info['zoneId']
|
||||
|
||||
return ret_dict
|
||||
|
||||
@@ -240,6 +192,7 @@ class decort_k8s(DecortController):
|
||||
self.amodule.params['extnet_only'],
|
||||
master_chipset,
|
||||
lb_sysctl=self.amodule.params['lb_sysctl'],
|
||||
zone_id=self.aparams['zone_id'],
|
||||
)
|
||||
|
||||
if not k8s_id:
|
||||
@@ -270,7 +223,7 @@ class decort_k8s(DecortController):
|
||||
self.k8s_should_exist = False
|
||||
return
|
||||
|
||||
def action(self, disared_state, started=True, preupdate: bool = False):
|
||||
def action(self, disared_state, preupdate: bool = False):
|
||||
if self.amodule.params['master_chipset'] is not None:
|
||||
for master_node in self.k8s_info['k8sGroups']['masters'][
|
||||
'detailedInfo'
|
||||
@@ -292,14 +245,19 @@ class decort_k8s(DecortController):
|
||||
# K8s info updating
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
#k8s state
|
||||
self.k8s_state(self.k8s_info, disared_state, started)
|
||||
self.k8s_state(self.k8s_info, disared_state)
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
if started == True and self.k8s_info['techStatus'] == "STOPPED":
|
||||
self.k8s_state(self.k8s_info, disared_state,started)
|
||||
self.k8s_info['techStatus'] == "STARTED"
|
||||
#check groups and modify if needed
|
||||
if self.aparams['workers'] is not None:
|
||||
self.k8s_workers_modify(self.k8s_info, self.amodule.params['workers'])
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.k8s_info['zoneId']:
|
||||
self.k8s_migrate_to_zone(
|
||||
k8s_id=self.k8s_id,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
if self.result['changed'] == True:
|
||||
self.k8s_info = self.k8s_get_by_id(k8s_id=self.k8s_id)
|
||||
#TODO check workers metadata and modify if needed
|
||||
@@ -328,17 +286,14 @@ class decort_k8s(DecortController):
|
||||
'disabled',
|
||||
'enabled',
|
||||
'present',
|
||||
'check',
|
||||
'started',
|
||||
'stopped',
|
||||
],
|
||||
),
|
||||
permanent=dict(
|
||||
type='bool',
|
||||
default=False,
|
||||
),
|
||||
started=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
name=dict(
|
||||
type='str',
|
||||
default='',
|
||||
@@ -490,18 +445,110 @@ class decort_k8s(DecortController):
|
||||
lb_sysctl=dict(
|
||||
type='dict',
|
||||
),
|
||||
zone_id=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
('id', 'name'),
|
||||
],
|
||||
)
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
|
||||
self.is_k8s_stopped_or_will_be_stopped = (
|
||||
(
|
||||
self.k8s_info['techStatus'] == 'STOPPED'
|
||||
and (
|
||||
self.aparams['state'] is None
|
||||
or self.aparams['state'] in ('present', 'stopped')
|
||||
)
|
||||
)
|
||||
or (
|
||||
self.k8s_info['techStatus'] != 'STOPPED'
|
||||
and self.aparams['state'] == 'stopped'
|
||||
)
|
||||
)
|
||||
|
||||
aparam_sysctl = self.aparams['lb_sysctl']
|
||||
if aparam_sysctl is not None:
|
||||
_, lb_info = self._lb_get_by_id(lb_id=self.k8s_info['lbId'])
|
||||
sysctl_with_str_values = {
|
||||
k: str(v) for k, v in aparam_sysctl.items()
|
||||
}
|
||||
if sysctl_with_str_values != lb_info['sysctlParams']:
|
||||
self.message(
|
||||
'Check for parameter "lb_sysctl" failed: '
|
||||
'cannot change lb_sysctl for an existing cluster '
|
||||
'load balancer.'
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
if (
|
||||
self.aparams['zone_id'] is not None
|
||||
and self.aparams['zone_id'] != self.k8s_info['zoneId']
|
||||
and not self.is_k8s_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "zone_id" failed: '
|
||||
'K8s cluster must be stopped to migrate to a zone.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
|
||||
validated_k8ci_id = self.k8s_k8ci_find(self.aparams['k8ci_id'])
|
||||
if not validated_k8ci_id:
|
||||
self.message(f'Cannot find K8CI ID {"k8ci_id"}.')
|
||||
check_errors = True
|
||||
|
||||
if not self.aparams['workers']:
|
||||
self.message('At least one worker group must be present.')
|
||||
check_errors = True
|
||||
|
||||
if (
|
||||
self.aparams['lb_sysctl'] is not None
|
||||
and not self.aparams['with_lb']
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "lb_sysctl" failed: '
|
||||
'"lb_sysctl" can only be set if the parameter "with_lb" '
|
||||
'is set to True.'
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
if (
|
||||
self.aparams['master_count'] is not None
|
||||
and self.aparams['master_count'] > 1
|
||||
and not self.aparams['with_lb']
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "master_count" failed: '
|
||||
'master_count can be more than 1 only if the parameter '
|
||||
'"with_lb" is set to True.'
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
|
||||
def main():
|
||||
subj = decort_k8s()
|
||||
amodule = subj.amodule
|
||||
|
||||
if amodule.params['state'] == 'check':
|
||||
if subj.amodule.check_mode:
|
||||
subj.result['changed'] = False
|
||||
if subj.k8s_id:
|
||||
# cluster is found - package facts and report success to Ansible
|
||||
@@ -521,7 +568,9 @@ def main():
|
||||
"ENABLING","DISABLING","RESTORING","MODELED"):
|
||||
subj.error()
|
||||
elif subj.k8s_info['status'] == "DELETED":
|
||||
if amodule.params['state'] in ('disabled', 'enabled', 'present'):
|
||||
if amodule.params['state'] in (
|
||||
'disabled', 'enabled', 'present', 'started', 'stopped'
|
||||
):
|
||||
subj.k8s_restore(subj.k8s_id)
|
||||
subj.action(disared_state=amodule.params['state'],
|
||||
preupdate=True)
|
||||
@@ -530,24 +579,15 @@ def main():
|
||||
subj.destroy()
|
||||
else:
|
||||
subj.nop()
|
||||
elif subj.k8s_info['techStatus'] in ("STARTED","STOPPED"):
|
||||
if amodule.params['state'] == 'disabled':
|
||||
subj.action(amodule.params['state'])
|
||||
elif amodule.params['state'] == 'absent':
|
||||
subj.destroy()
|
||||
else:
|
||||
subj.action(amodule.params['state'],amodule.params['started'])
|
||||
elif subj.k8s_info['status'] == "DISABLED":
|
||||
elif subj.k8s_info['status'] in ('ENABLED', 'DISABLED'):
|
||||
if amodule.params['state'] == 'absent':
|
||||
subj.destroy()
|
||||
elif amodule.params['state'] in ('present','enabled'):
|
||||
subj.action(amodule.params['state'],amodule.params['started'])
|
||||
else:
|
||||
subj.nop()
|
||||
elif subj.k8s_info['status'] == "DESTROED":
|
||||
subj.action(disared_state=amodule.params['state'])
|
||||
elif subj.k8s_info['status'] == "DESTROYED":
|
||||
if amodule.params['state'] in ('present','enabled'):
|
||||
subj.create()
|
||||
if amodule.params['state'] == 'absent':
|
||||
if amodule.params['state'] == 'absent':
|
||||
subj.nop()
|
||||
else:
|
||||
if amodule.params['state'] == 'absent':
|
||||
|
||||
@@ -18,6 +18,9 @@ DefaultT = TypeVar('DefaultT')
|
||||
|
||||
|
||||
class decort_kvmvm(DecortController):
|
||||
is_vm_stopped_or_will_be_stopped: None | bool = None
|
||||
guest_agent_exec_result: None | str = None
|
||||
|
||||
def __init__(self):
|
||||
# call superclass constructor first
|
||||
super(decort_kvmvm, self).__init__(AnsibleModule(**self.amodule_init_args))
|
||||
@@ -31,20 +34,22 @@ class decort_kvmvm(DecortController):
|
||||
# This following flag is used to avoid extra (and unnecessary) get of compute details prior to
|
||||
# packaging facts before the module completes. As ""
|
||||
self.skip_final_get = False
|
||||
self.force_final_get = False
|
||||
self.comp_id = 0
|
||||
self.comp_info = None
|
||||
self.acc_id = 0
|
||||
self.rg_id = 0
|
||||
self.aparam_image = None
|
||||
|
||||
|
||||
validated_acc_id =0
|
||||
validated_acc_id = 0
|
||||
validated_rg_id = 0
|
||||
validated_rg_facts = None
|
||||
|
||||
self.vm_to_clone_id = 0
|
||||
self.vm_to_clone_info = None
|
||||
|
||||
if self.aparams['get_snapshot_merge_status']:
|
||||
self.force_final_get = True
|
||||
|
||||
if arg_amodule.params['clone_from'] is not None:
|
||||
self.vm_to_clone_id, self.vm_to_clone_info, _ = (
|
||||
self._compute_get_by_id(
|
||||
@@ -101,7 +106,7 @@ class decort_kvmvm(DecortController):
|
||||
|
||||
if not comp_id: # manage Compute by name -> need RG identity
|
||||
if not arg_amodule.params['rg_id']: # RG ID is not set -> locate RG by name -> need account ID
|
||||
validated_acc_id, _ = self.account_find(arg_amodule.params['account_name'],
|
||||
validated_acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'],
|
||||
arg_amodule.params['account_id'])
|
||||
if not validated_acc_id:
|
||||
self.result['failed'] = True
|
||||
@@ -142,6 +147,7 @@ class decort_kvmvm(DecortController):
|
||||
if self.comp_id:
|
||||
self.comp_should_exist = True
|
||||
self.acc_id = self.comp_info['accountId']
|
||||
self.rg_id = self.comp_info['rgId']
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
if self.amodule.params['state'] != 'absent':
|
||||
@@ -186,18 +192,82 @@ class decort_kvmvm(DecortController):
|
||||
'to a DPDK network.'
|
||||
)
|
||||
for net in aparam_nets:
|
||||
# MTU for non-DPDK networks
|
||||
net_type = net['type']
|
||||
|
||||
if (
|
||||
net['type'] != self.VMNetType.DPDK.value
|
||||
and net['mtu'] is not None
|
||||
net['type'] not in (
|
||||
self.VMNetType.SDN.value,
|
||||
self.VMNetType.EMPTY.value,
|
||||
)
|
||||
and not isinstance(net['id'], int)
|
||||
):
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed:'
|
||||
' MTU can be specifed only for DPDK network'
|
||||
' (remove parameter "mtu" for network'
|
||||
f' {net["type"]} with ID {net["id"]}).'
|
||||
'Check for parameter "networks" failed: '
|
||||
'Type of parameter "id" must be integer for '
|
||||
f'{net["type"]} network type'
|
||||
)
|
||||
|
||||
# MTU
|
||||
net_mtu = net['mtu']
|
||||
if net_mtu is not None:
|
||||
mtu_net_types = (
|
||||
self.VMNetType.DPDK.value,
|
||||
self.VMNetType.EXTNET.value,
|
||||
)
|
||||
|
||||
# Allowed network types for set MTU
|
||||
if net_type not in mtu_net_types:
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed:'
|
||||
' MTU can be specifed'
|
||||
' only for DPDK or EXTNET network'
|
||||
' (remove parameter "mtu" for network'
|
||||
f' {net["type"]} with ID {net["id"]}).'
|
||||
)
|
||||
|
||||
# Maximum MTU
|
||||
MAX_MTU = 9216
|
||||
if net_type in mtu_net_types and net_mtu > MAX_MTU:
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed:'
|
||||
f' MTU must be no more than {MAX_MTU}'
|
||||
' (change value for parameter "mtu" for network'
|
||||
f' {net["type"]} with ID {net["id"]}).'
|
||||
)
|
||||
|
||||
# EXTNET minimum MTU
|
||||
EXTNET_MIN_MTU = 1500
|
||||
if (
|
||||
net_type == self.VMNetType.EXTNET.value
|
||||
and net_mtu < EXTNET_MIN_MTU
|
||||
):
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed:'
|
||||
f' MTU for {self.VMNetType.EXTNET.value} network'
|
||||
f' must be at least {EXTNET_MIN_MTU}'
|
||||
' (change value for parameter "mtu" for network'
|
||||
f' {net["type"]} with ID {net["id"]}).'
|
||||
)
|
||||
|
||||
# DPDK minimum MTU
|
||||
DPDK_MIN_MTU = 1
|
||||
if (
|
||||
net_type == self.VMNetType.DPDK.value
|
||||
and net_mtu < DPDK_MIN_MTU
|
||||
):
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed:'
|
||||
f' MTU for {self.VMNetType.DPDK.value} network'
|
||||
f' must be at least {DPDK_MIN_MTU}'
|
||||
' (change value for parameter "mtu" for network'
|
||||
f' {net["type"]} with ID {net["id"]}).'
|
||||
)
|
||||
|
||||
# MAC address
|
||||
if net['mac'] is not None:
|
||||
if net['type'] == self.VMNetType.EMPTY.value:
|
||||
@@ -220,7 +290,20 @@ class decort_kvmvm(DecortController):
|
||||
'specified in quotes and in the format '
|
||||
'"XX:XX:XX:XX:XX:XX".'
|
||||
)
|
||||
|
||||
if self.VMNetType.SDN.value in net_types:
|
||||
if not net_types.issubset(
|
||||
{
|
||||
self.VMNetType.SDN.value,
|
||||
self.VMNetType.EMPTY.value,
|
||||
self.VMNetType.VFNIC.value,
|
||||
}
|
||||
):
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
'a compute can be connected to a SDN network and '
|
||||
'only to VFNIC, EMPTY networks at the same time.'
|
||||
)
|
||||
aparam_custom_fields = self.aparams['custom_fields']
|
||||
if aparam_custom_fields is not None:
|
||||
if (
|
||||
@@ -458,7 +541,8 @@ class decort_kvmvm(DecortController):
|
||||
boot_mode=boot_mode,
|
||||
boot_loader_type=loader_type,
|
||||
network_interface_naming=network_interface_naming,
|
||||
hot_resize=hot_resize,)
|
||||
hot_resize=hot_resize,
|
||||
zone_id=self.aparams['zone_id'],)
|
||||
self.comp_should_exist = True
|
||||
|
||||
# Originally we would have had to re-read comp_info after VM was provisioned
|
||||
@@ -636,6 +720,40 @@ class decort_kvmvm(DecortController):
|
||||
custom_fields=aparam_custom_fields['fields'],
|
||||
)
|
||||
|
||||
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,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
aparam_guest_agent = self.aparams['guest_agent']
|
||||
if aparam_guest_agent is not None:
|
||||
if aparam_guest_agent['enabled'] is not None:
|
||||
if (
|
||||
aparam_guest_agent['enabled']
|
||||
and not self.comp_info['qemu_guest']['enabled']
|
||||
):
|
||||
self.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)
|
||||
|
||||
if aparam_guest_agent['update_available_commands']:
|
||||
self.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:
|
||||
self.guest_agent_exec_result = (
|
||||
self.compute_guest_agent_execute(
|
||||
vm_id=self.comp_id,
|
||||
cmd=aparam_guest_agent_exec['cmd'],
|
||||
args=aparam_guest_agent_exec['args'],
|
||||
)
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@property
|
||||
@@ -779,7 +897,7 @@ class decort_kvmvm(DecortController):
|
||||
# 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'])
|
||||
|
||||
|
||||
ret_dict['cpu'] = self.comp_info['cpus']
|
||||
ret_dict['ram'] = self.comp_info['ram']
|
||||
|
||||
@@ -830,6 +948,18 @@ class decort_kvmvm(DecortController):
|
||||
ret_dict['affinity_rules'] = self.comp_info['affinityRules']
|
||||
ret_dict['anti_affinity_rules'] = self.comp_info['antiAffinityRules']
|
||||
|
||||
ret_dict['zone_id'] = self.comp_info['zoneId']
|
||||
|
||||
ret_dict['guest_agent'] = self.comp_info['qemu_guest']
|
||||
|
||||
if self.guest_agent_exec_result:
|
||||
ret_dict['guest_agent']['exec_result'] = self.guest_agent_exec_result # noqa: E501
|
||||
|
||||
if self.amodule.params['get_snapshot_merge_status']:
|
||||
ret_dict['snapshot_merge_status'] = (
|
||||
self.comp_info['snapshot_merge_status']
|
||||
)
|
||||
|
||||
return ret_dict
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
@@ -918,6 +1048,30 @@ class decort_kvmvm(DecortController):
|
||||
' to a DPDK network.'
|
||||
)
|
||||
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if self.aparams['guest_agent'] is not None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "guest_agent" failed: '
|
||||
'guest_agent can be specified only for existing VM.'
|
||||
)
|
||||
|
||||
if self.aparams['get_snapshot_merge_status']:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "get_snapshot_merge_status" failed: '
|
||||
'snapshot merge status can be retrieved only for existing VM.'
|
||||
)
|
||||
|
||||
aparam_networks = self.aparams['networks']
|
||||
if aparam_networks is not None:
|
||||
net_types = {net['type'] for net in aparam_networks}
|
||||
if self.VMNetType.TRUNK.value in net_types:
|
||||
if self.check_aparam_networks_trunk() is False:
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@@ -1019,11 +1173,13 @@ class decort_kvmvm(DecortController):
|
||||
'EXTNET',
|
||||
'VFNIC',
|
||||
'DPDK',
|
||||
'TRUNK',
|
||||
'SDN',
|
||||
'EMPTY',
|
||||
],
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
type='raw',
|
||||
),
|
||||
ip_addr=dict(
|
||||
type='str',
|
||||
@@ -1040,6 +1196,8 @@ class decort_kvmvm(DecortController):
|
||||
('type', 'EXTNET', ('id',)),
|
||||
('type', 'VFNIC', ('id',)),
|
||||
('type', 'DPDK', ('id',)),
|
||||
('type', 'TRUNK', ('id',)),
|
||||
('type', 'SDN', ('id', 'mac')),
|
||||
],
|
||||
),
|
||||
network_order_changing=dict(
|
||||
@@ -1180,6 +1338,36 @@ class decort_kvmvm(DecortController):
|
||||
hot_resize=dict(
|
||||
type='bool',
|
||||
),
|
||||
zone_id=dict(
|
||||
type='int',
|
||||
),
|
||||
guest_agent=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
enabled=dict(
|
||||
type='bool',
|
||||
),
|
||||
exec=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
cmd=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
args=dict(
|
||||
type='dict',
|
||||
default={},
|
||||
),
|
||||
),
|
||||
),
|
||||
update_available_commands=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
),
|
||||
get_snapshot_merge_status=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
@@ -1196,6 +1384,24 @@ class decort_kvmvm(DecortController):
|
||||
comp_info = self.vm_to_clone_info or self.comp_info
|
||||
comp_id = comp_info['id']
|
||||
|
||||
self.is_vm_stopped_or_will_be_stopped = (
|
||||
(
|
||||
comp_info['techStatus'] == 'STOPPED'
|
||||
and (
|
||||
self.amodule.params['state'] is None
|
||||
or self.amodule.params['state'] in (
|
||||
'halted', 'poweredoff', 'present', 'stopped',
|
||||
)
|
||||
)
|
||||
)
|
||||
or (
|
||||
comp_info['techStatus'] != 'STOPPED'
|
||||
and self.amodule.params['state'] in (
|
||||
'halted', 'poweredoff', 'stopped',
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
aparam_boot = self.amodule.params['boot']
|
||||
if aparam_boot is not None:
|
||||
new_boot_disk_size = aparam_boot['disk_size']
|
||||
@@ -1285,26 +1491,8 @@ class decort_kvmvm(DecortController):
|
||||
'state for a blank Compute can not be "started" or "paused".'
|
||||
)
|
||||
|
||||
is_vm_stopped_or_will_be_stopped = (
|
||||
(
|
||||
comp_info['techStatus'] == 'STOPPED'
|
||||
and (
|
||||
self.amodule.params['state'] is None
|
||||
or self.amodule.params['state'] in (
|
||||
'halted', 'poweredoff', 'present', 'stopped',
|
||||
)
|
||||
)
|
||||
)
|
||||
or (
|
||||
comp_info['techStatus'] != 'STOPPED'
|
||||
and self.amodule.params['state'] in (
|
||||
'halted', 'poweredoff', 'stopped',
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if self.amodule.params['rollback_to'] is not None:
|
||||
if not is_vm_stopped_or_will_be_stopped:
|
||||
if not self.is_vm_stopped_or_will_be_stopped:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "rollback_to" failed: '
|
||||
@@ -1334,7 +1522,7 @@ class decort_kvmvm(DecortController):
|
||||
if (
|
||||
self.aparams[param_name] is not None
|
||||
and comp_info[comp_field_name] != self.aparams[param_name]
|
||||
and not is_vm_stopped_or_will_be_stopped
|
||||
and not self.is_vm_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -1343,7 +1531,7 @@ class decort_kvmvm(DecortController):
|
||||
)
|
||||
|
||||
if self.aparams['preferred_cpu_cores'] is not None:
|
||||
if not is_vm_stopped_or_will_be_stopped:
|
||||
if not self.is_vm_stopped_or_will_be_stopped:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "preferred_cpu_cores" failed: '
|
||||
@@ -1407,7 +1595,7 @@ class decort_kvmvm(DecortController):
|
||||
if (
|
||||
comp_boot_disk_id is not None
|
||||
and comp_boot_disk_id in disks_to_detach
|
||||
and not is_vm_stopped_or_will_be_stopped
|
||||
and not self.is_vm_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
@@ -1438,6 +1626,22 @@ class decort_kvmvm(DecortController):
|
||||
'Hot resize must be enabled to change CPU or RAM.'
|
||||
)
|
||||
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if self.check_aparam_guest_agent() is False:
|
||||
check_errors = True
|
||||
|
||||
if self.check_aparam_get_snapshot_merge_status() is False:
|
||||
check_errors = True
|
||||
|
||||
aparam_networks = self.aparams['networks']
|
||||
if aparam_networks is not None:
|
||||
net_types = {net['type'] for net in aparam_networks}
|
||||
if self.VMNetType.TRUNK.value in net_types:
|
||||
if self.check_aparam_networks_trunk() is False:
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
@@ -1530,6 +1734,211 @@ class decort_kvmvm(DecortController):
|
||||
)
|
||||
return clone_id
|
||||
|
||||
def check_aparam_guest_agent(self) -> bool:
|
||||
check_errors = False
|
||||
aparam_guest_agent = self.aparams['guest_agent']
|
||||
if aparam_guest_agent:
|
||||
if self.is_vm_stopped_or_will_be_stopped:
|
||||
if aparam_guest_agent['update_available_commands']:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter '
|
||||
'"guest_agent.update_available_commands" failed: '
|
||||
f'VM ID {self.comp_id} must be started to update '
|
||||
'available commands.'
|
||||
)
|
||||
|
||||
is_guest_agent_enabled_or_will_be_enabled = (
|
||||
(
|
||||
self.comp_info['qemu_guest']['enabled']
|
||||
and aparam_guest_agent['enabled'] is not False
|
||||
)
|
||||
or (
|
||||
self.comp_info['qemu_guest']['enabled'] is False
|
||||
and aparam_guest_agent['enabled']
|
||||
)
|
||||
)
|
||||
|
||||
aparam_guest_agent_exec = aparam_guest_agent['exec']
|
||||
if aparam_guest_agent_exec is not None:
|
||||
if self.is_vm_stopped_or_will_be_stopped:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "guest_agent.exec" failed: '
|
||||
f'VM ID {self.comp_id} must be started '
|
||||
'to execute commands.'
|
||||
)
|
||||
|
||||
if not is_guest_agent_enabled_or_will_be_enabled:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "guest_agent.exec" failed: '
|
||||
f'Guest agent for VM ID {self.comp_id} must be enabled'
|
||||
' to execute commands.'
|
||||
)
|
||||
|
||||
aparam_exec_cmd = aparam_guest_agent_exec['cmd']
|
||||
available_commands = (
|
||||
self.comp_info['qemu_guest']['enabled_agent_features']
|
||||
)
|
||||
if aparam_exec_cmd not in available_commands:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "guest_agent.exec.cmd" failed: '
|
||||
f'Command "{aparam_exec_cmd}" is not '
|
||||
f'available for VM ID {self.comp_id}.'
|
||||
)
|
||||
|
||||
return not check_errors
|
||||
|
||||
def check_aparam_get_snapshot_merge_status(self) -> bool | None:
|
||||
check_errors = False
|
||||
if self.aparams['get_snapshot_merge_status']:
|
||||
vm_has_shared_sep_disk = False
|
||||
vm_disk_ids = [disk['id'] for disk in self.comp_info['disks']]
|
||||
for disk_id in vm_disk_ids:
|
||||
_, disk_info = self._disk_get_by_id(disk_id=disk_id)
|
||||
if disk_info['sepType'] == 'SHARED':
|
||||
vm_has_shared_sep_disk = True
|
||||
break
|
||||
|
||||
if not vm_has_shared_sep_disk:
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "get_snapshot_merge_status" failed: '
|
||||
f'VM ID {self.comp_id} must have at least one disk with '
|
||||
'SEP type SHARED to retrieve snapshot merge status.'
|
||||
)
|
||||
|
||||
return not check_errors
|
||||
|
||||
def find_networks_tags_intersections(
|
||||
self,
|
||||
trunk_networks: list,
|
||||
extnet_networks: list,
|
||||
) -> bool:
|
||||
has_intersections = False
|
||||
|
||||
def parse_trunk_tags(trunk_tags_string: str):
|
||||
trunk_tags = set()
|
||||
for part in trunk_tags_string.split(','):
|
||||
if '-' in part:
|
||||
start, end = part.split('-')
|
||||
trunk_tags.update(range(int(start), int(end) + 1))
|
||||
else:
|
||||
trunk_tags.add(int(part))
|
||||
return trunk_tags
|
||||
|
||||
trunk_tags_dicts = []
|
||||
for trunk_network in trunk_networks:
|
||||
trunk_tags_dicts.append({
|
||||
'id': trunk_network['id'],
|
||||
'tags_str': trunk_network['trunkTags'],
|
||||
'tags': parse_trunk_tags(
|
||||
trunk_tags_string=trunk_network['trunkTags']
|
||||
),
|
||||
'native_vlan_id': trunk_network['nativeVlanId'],
|
||||
})
|
||||
|
||||
# find for trunk tags intersections with other networks
|
||||
for i in range(len(trunk_tags_dicts)):
|
||||
for j in range(i + 1, len(trunk_tags_dicts)):
|
||||
intersection = (
|
||||
trunk_tags_dicts[i]['tags']
|
||||
& trunk_tags_dicts[j]['tags']
|
||||
)
|
||||
if intersection:
|
||||
has_intersections = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
f'Trunk tags {trunk_tags_dicts[i]["tags_str"]} '
|
||||
f'of trunk ID {trunk_tags_dicts[i]["id"]} '
|
||||
f'overlaps with trunk tags '
|
||||
f'{trunk_tags_dicts[j]["tags_str"]} of trunk ID '
|
||||
f'{trunk_tags_dicts[j]["id"]}'
|
||||
)
|
||||
for extnet in extnet_networks:
|
||||
if extnet['vlanId'] in trunk_tags_dicts[i]['tags']:
|
||||
has_intersections = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
f'Trunk tags {trunk_tags_dicts[i]["tags_str"]} '
|
||||
f'of trunk ID {trunk_tags_dicts[i]["id"]} '
|
||||
f'overlaps with tag {extnet["vlanId"]} of extnet ID '
|
||||
f'{extnet["id"]}'
|
||||
)
|
||||
if extnet['vlanId'] == trunk_tags_dicts[i]['native_vlan_id']:
|
||||
has_intersections = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
f'Trunk native vlan ID '
|
||||
f'{trunk_tags_dicts[i]["native_vlan_id"]} of trunk ID '
|
||||
f'{trunk_tags_dicts[i]["id"]} '
|
||||
f'overlaps with vlan ID {extnet["vlanId"]} of extnet '
|
||||
f'ID {extnet["id"]}'
|
||||
)
|
||||
|
||||
return has_intersections
|
||||
|
||||
def check_aparam_networks_trunk(self) -> bool | None:
|
||||
check_errors = False
|
||||
|
||||
# check if account has vm feature “trunk”
|
||||
if not self.check_account_vm_features(vm_feature=self.VMFeature.trunk):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
f'Account ID {self.acc_id} must have feature "trunk" to use '
|
||||
'trunk type networks '
|
||||
)
|
||||
# check if rg has vm feature “trunk”
|
||||
if not self.check_rg_vm_features(vm_feature=self.VMFeature.trunk):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
f'RG ID {self.rg_id} must have feature "trunk" to use '
|
||||
'trunk type networks '
|
||||
)
|
||||
|
||||
aparam_trunk_networks = []
|
||||
aparam_extnet_networks = []
|
||||
for net in self.aparams['networks']:
|
||||
if net['type'] == self.VMNetType.TRUNK.value:
|
||||
aparam_trunk_networks.append(net)
|
||||
elif net['type'] == self.VMNetType.EXTNET.value:
|
||||
aparam_extnet_networks.append(net)
|
||||
|
||||
trunk_networks_info = []
|
||||
# check that account has access to all specified trunks
|
||||
for trunk_network in aparam_trunk_networks:
|
||||
trunk_info = self.trunk_get(id=trunk_network['id'])
|
||||
trunk_networks_info.append(trunk_info)
|
||||
if (
|
||||
trunk_info['accountIds'] is None
|
||||
or self.acc_id not in trunk_info['accountIds']
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: '
|
||||
f'Account ID {self.acc_id} does not have access to '
|
||||
f'trunk ID {trunk_info['id']}'
|
||||
)
|
||||
|
||||
extnet_networks_info = []
|
||||
for extnet_network in aparam_extnet_networks:
|
||||
extnet_networks_info.append(
|
||||
self.extnet_get(id=extnet_network['id'])
|
||||
)
|
||||
# check that trunk tags do not overlap with each other
|
||||
# and with extnets vlan id
|
||||
if self.find_networks_tags_intersections(
|
||||
trunk_networks=trunk_networks_info,
|
||||
extnet_networks=extnet_networks_info,
|
||||
):
|
||||
check_errors = True
|
||||
|
||||
return not check_errors
|
||||
|
||||
# Workflow digest:
|
||||
# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECSController
|
||||
# 2) check if the VM with the specified id or rg_name:name exists
|
||||
@@ -1617,12 +2026,16 @@ def main():
|
||||
# prepare Compute facts to be returned as part of decon.result and then call exit_json(...)
|
||||
rg_facts = None
|
||||
if subj.comp_should_exist:
|
||||
if subj.result['changed'] and not subj.skip_final_get:
|
||||
if (
|
||||
(subj.result['changed'] and not subj.skip_final_get)
|
||||
or subj.force_final_get
|
||||
):
|
||||
# There were changes to the Compute - refresh Compute facts.
|
||||
_, subj.comp_info, _ = subj.compute_find(
|
||||
comp_id=subj.comp_id,
|
||||
need_custom_fields=True,
|
||||
need_console_url=amodule.params['get_console_url'],
|
||||
need_snapshot_merge_status=amodule.params['get_snapshot_merge_status'], # noqa: E501
|
||||
)
|
||||
#
|
||||
# We no longer need to re-read RG facts, as all network info is now available inside
|
||||
|
||||
@@ -23,8 +23,6 @@ class decort_lb(DecortController):
|
||||
self.vins_facts = None
|
||||
self.rg_id = 0
|
||||
self.rg_facts = None
|
||||
self.acc_id = 0
|
||||
self.acc_facts = None
|
||||
self.default_server_check = "enabled"
|
||||
self.default_alg = "roundrobin"
|
||||
self.default_settings = {
|
||||
@@ -37,6 +35,8 @@ class decort_lb(DecortController):
|
||||
"slowstart": 60000,
|
||||
"weight": 100,
|
||||
}
|
||||
self.is_lb_stopped_or_will_be_stopped: None | bool = None
|
||||
|
||||
if arg_amodule.params['lb_id']:
|
||||
self.lb_id, self.lb_facts = self.lb_find(arg_amodule.params['lb_id'])
|
||||
if not self.lb_id:
|
||||
@@ -46,14 +46,14 @@ class decort_lb(DecortController):
|
||||
self.fail_json(**self.result)
|
||||
self.rg_id = self.lb_facts['rgId']
|
||||
self.vins_id = self.lb_facts['vinsId']
|
||||
return
|
||||
|
||||
if arg_amodule.params['rg_id']:
|
||||
|
||||
elif arg_amodule.params['rg_id']:
|
||||
self.rg_id, self.rg_facts = self.rg_find(0,arg_amodule.params['rg_id'], arg_rg_name="")
|
||||
if not self.rg_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "Specified RG ID {} not found.".format(arg_amodule.params['vins_id'])
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.acc_id = self.rg_facts['accountId']
|
||||
|
||||
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
|
||||
|
||||
@@ -61,7 +61,7 @@ class decort_lb(DecortController):
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = ("RG name must be specified with account present")
|
||||
self.amodule.fail_json(**self.result)
|
||||
self.acc_id, self.acc_facts = self.account_find(arg_amodule.params['account_name'],
|
||||
self.acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'],
|
||||
arg_amodule.params['account_id'])
|
||||
if not self.acc_id:
|
||||
self.result['failed'] = True
|
||||
@@ -96,15 +96,26 @@ class decort_lb(DecortController):
|
||||
|
||||
if self.rg_id and arg_amodule.params['lb_name']:
|
||||
self.lb_id, self.lb_facts = self.lb_find(0,arg_amodule.params['lb_name'],self.rg_id)
|
||||
|
||||
if self.lb_id and self.lb_facts['status'] != 'DESTROYED':
|
||||
self.acc_id = self.lb_facts['accountId']
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
|
||||
return
|
||||
|
||||
|
||||
def create(self):
|
||||
start_after_create = self.aparams['state'] != 'stopped'
|
||||
self.lb_id = self.lb_provision(self.amodule.params['lb_name'],
|
||||
self.rg_id,self.vins_id,
|
||||
self.amodule.params['ext_net_id'],
|
||||
self.amodule.params['ha_lb'],
|
||||
self.amodule.params['description'],
|
||||
sysctl=self.amodule.params['sysctl'],)
|
||||
sysctl=self.amodule.params['sysctl'],
|
||||
zone_id=self.aparams['zone_id'],
|
||||
start=start_after_create,
|
||||
)
|
||||
if self.lb_id and (self.amodule.params['backends'] or
|
||||
self.amodule.params['frontends']):
|
||||
self.lb_id, self.lb_facts = self.lb_find(0,self.amodule.params['lb_name'],self.rg_id)
|
||||
@@ -141,6 +152,13 @@ class decort_lb(DecortController):
|
||||
self.lb_state(self.lb_facts, 'started')
|
||||
_, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.lb_facts['zoneId']:
|
||||
self.lb_migrate_to_zone(
|
||||
lb_id=self.lb_id,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
def delete(self):
|
||||
@@ -205,13 +223,15 @@ class decort_lb(DecortController):
|
||||
ret_dict['id'] = self.lb_facts['id']
|
||||
ret_dict['name'] = self.lb_facts['name']
|
||||
ret_dict['state'] = self.lb_facts['status']
|
||||
#ret_dict['account_id'] = self.lb_facts['accountId']
|
||||
ret_dict['account_id'] = self.lb_facts['accountId']
|
||||
ret_dict['rg_id'] = self.lb_facts['rgId']
|
||||
ret_dict['gid'] = self.lb_facts['gid']
|
||||
if self.amodule.params['state']!="absent":
|
||||
ret_dict['backends'] = self.lb_facts['backends']
|
||||
ret_dict['frontends'] = self.lb_facts['frontends']
|
||||
ret_dict['sysctl'] = self.lb_facts['sysctlParams']
|
||||
ret_dict['zone_id'] = self.lb_facts['zoneId']
|
||||
ret_dict['tech_status'] = self.lb_facts['techStatus']
|
||||
return ret_dict
|
||||
|
||||
@property
|
||||
@@ -227,11 +247,10 @@ class decort_lb(DecortController):
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
default='Managed by Ansible module decort_lb',
|
||||
),
|
||||
ext_net_id=dict(
|
||||
type='int',
|
||||
default=-1,
|
||||
default=0,
|
||||
),
|
||||
ext_ip_addr=dict(
|
||||
type='str',
|
||||
@@ -239,13 +258,14 @@ class decort_lb(DecortController):
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
default='present',
|
||||
choices=[
|
||||
'absent',
|
||||
'disabled',
|
||||
'enabled',
|
||||
'present',
|
||||
'restart',
|
||||
'started',
|
||||
'stopped',
|
||||
],
|
||||
),
|
||||
rg_id=dict(
|
||||
@@ -291,6 +311,9 @@ class decort_lb(DecortController):
|
||||
sysctl=dict(
|
||||
type='dict',
|
||||
),
|
||||
zone_id=dict(
|
||||
type=int,
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
@@ -300,6 +323,49 @@ class decort_lb(DecortController):
|
||||
],
|
||||
)
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
|
||||
lb_info: dict = self.lb_facts
|
||||
self.is_lb_stopped_or_will_be_stopped = (
|
||||
(
|
||||
lb_info['techStatus'] == 'STOPPED'
|
||||
and (
|
||||
self.aparams['state'] is None
|
||||
or self.aparams['state'] in ('present', 'stopped')
|
||||
)
|
||||
)
|
||||
or (
|
||||
lb_info['techStatus'] != 'STOPPED'
|
||||
and self.aparams['state'] == 'stopped'
|
||||
)
|
||||
)
|
||||
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
if (
|
||||
self.aparams['zone_id'] is not None
|
||||
and self.aparams['zone_id'] != lb_info['zoneId']
|
||||
and not self.is_lb_stopped_or_will_be_stopped
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "zone_id" failed: '
|
||||
'Load balancer must be stopped to migrate to a zone.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
|
||||
def main():
|
||||
decon = decort_lb()
|
||||
amodule = decon.amodule
|
||||
@@ -309,24 +375,15 @@ def main():
|
||||
decon.result['changed'] = False
|
||||
decon.result['msg'] = ("No change can be done for existing LB ID {} because of its current "
|
||||
"status '{}'").format(decon.lb_id, decon.lb_facts['status'])
|
||||
elif decon.lb_facts['status'] == "DISABLED":
|
||||
if amodule.params['state'] == 'absent':
|
||||
decon.delete()
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
decon.action()
|
||||
elif amodule.params['state'] in ('enabled', 'present'):
|
||||
decon.action('enabled')
|
||||
elif decon.lb_facts['status'] in ["CREATED", "ENABLED"]:
|
||||
if amodule.params['state'] == 'absent':
|
||||
decon.delete()
|
||||
elif amodule.params['state'] in ('present', 'enabled'):
|
||||
decon.action(d_state='enabled')
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
decon.action('disabled')
|
||||
elif amodule.params['state'] in ('stopped', 'started','restart'):
|
||||
decon.action(amodule.params['state'])
|
||||
elif decon.lb_facts['status'] in ('DISABLED', 'ENABLED', 'CREATED'):
|
||||
if amodule.params['state'] == 'absent':
|
||||
decon.delete()
|
||||
else:
|
||||
decon.action(d_state=amodule.params['state'])
|
||||
elif decon.lb_facts['status'] == "DELETED":
|
||||
if amodule.params['state'] in ['present', 'enabled']:
|
||||
if amodule.params['state'] == 'present':
|
||||
decon.action(restore=True)
|
||||
elif amodule.params['state'] == 'enabled':
|
||||
decon.action(d_state='enabled', restore=True)
|
||||
elif (amodule.params['state'] == 'absent' and
|
||||
amodule.params['permanently']):
|
||||
@@ -341,11 +398,14 @@ def main():
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
decon.error()
|
||||
else:
|
||||
if amodule.params['state'] == 'absent':
|
||||
state = amodule.params['state']
|
||||
if state is None:
|
||||
state = 'present'
|
||||
if state == 'absent':
|
||||
decon.nop()
|
||||
elif amodule.params['state'] in ('present', 'enabled'):
|
||||
elif state in ('present', 'enabled', 'stopped', 'started'):
|
||||
decon.create()
|
||||
elif amodule.params['state'] == 'disabled':
|
||||
elif state == 'disabled':
|
||||
decon.error()
|
||||
|
||||
if decon.result['failed']:
|
||||
@@ -357,4 +417,4 @@ def main():
|
||||
decon.result['facts'] = decon.package_facts(amodule.check_mode)
|
||||
amodule.exit_json(**decon.result)
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -26,7 +26,7 @@ class decort_osimage(DecortController):
|
||||
if amodule.params['account_name']:
|
||||
self.validated_account_id, _ = self.account_find(amodule.params['account_name'])
|
||||
else:
|
||||
self.validated_account_id = amodule.params['account_Id']
|
||||
self.validated_account_id = amodule.params['account_id']
|
||||
|
||||
if self.validated_account_id == 0:
|
||||
# we failed either to find or access the specified account - fail the module
|
||||
@@ -129,7 +129,7 @@ class decort_osimage(DecortController):
|
||||
hot_resize=hot_resize,
|
||||
username=amodule.params['image_username'],
|
||||
password=amodule.params['image_password'],
|
||||
account_Id=self.validated_account_id,
|
||||
account_id=self.validated_account_id,
|
||||
usernameDL=amodule.params['usernameDL'],
|
||||
passwordDL=amodule.params['passwordDL'],
|
||||
sepId=amodule.params['sepId'],
|
||||
@@ -156,7 +156,11 @@ class decort_osimage(DecortController):
|
||||
|
||||
def decort_virt_image_create(self,amodule):
|
||||
# function that creates a virtual image
|
||||
image_facts = self.virt_image_create(name=amodule.params['virt_name'], targetId=self.target_image_id)
|
||||
image_facts = self.virt_image_create(
|
||||
name=amodule.params['virt_name'],
|
||||
target_id=self.target_image_id,
|
||||
account_id=self.aparams['account_id'],
|
||||
)
|
||||
image_id, image_facts = decort_osimage.decort_virt_image_find(self, amodule)
|
||||
self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
|
||||
return image_id, image_facts
|
||||
@@ -244,7 +248,7 @@ class decort_osimage(DecortController):
|
||||
account_name=dict(
|
||||
type='str',
|
||||
),
|
||||
account_Id=dict(
|
||||
account_id=dict(
|
||||
type='int',
|
||||
),
|
||||
image_name=dict(
|
||||
|
||||
@@ -42,6 +42,9 @@ class decort_rg(DecortController):
|
||||
else:
|
||||
self.rg_should_exist = False
|
||||
|
||||
if self.validated_rg_id and self.rg_facts['status'] != 'DESTROYED':
|
||||
self.check_amodule_args_for_change()
|
||||
|
||||
def get_info(self):
|
||||
# If this is the first getting info
|
||||
if not self.validated_rg_id:
|
||||
@@ -150,18 +153,20 @@ class decort_rg(DecortController):
|
||||
return
|
||||
|
||||
def create(self):
|
||||
self.validated_rg_id = self.rg_provision(self.validated_acc_id,
|
||||
self.amodule.params['rg_name'],
|
||||
self.amodule.params['owner'],
|
||||
self.amodule.params['description'],
|
||||
self.amodule.params['resType'],
|
||||
self.amodule.params['def_netType'],
|
||||
self.amodule.params['ipcidr'],
|
||||
self.amodule.params['extNetId'],
|
||||
self.amodule.params['extNetIp'],
|
||||
self.amodule.params['quotas'],
|
||||
"", # this is location code. TODO: add module argument
|
||||
)
|
||||
self.validated_rg_id = self.rg_provision(
|
||||
self.validated_acc_id,
|
||||
self.amodule.params['rg_name'],
|
||||
self.amodule.params['owner'],
|
||||
self.amodule.params['description'],
|
||||
self.amodule.params['resType'],
|
||||
self.amodule.params['def_netType'],
|
||||
self.amodule.params['ipcidr'],
|
||||
self.amodule.params['extNetId'],
|
||||
self.amodule.params['extNetIp'],
|
||||
self.amodule.params['quotas'],
|
||||
"", # this is location code. TODO: add module argument
|
||||
sdn_access_group_id=self.aparams['sdn_access_group_id'],
|
||||
)
|
||||
|
||||
if self.validated_rg_id:
|
||||
self.validated_rg_id, self.rg_facts = self.rg_find(
|
||||
@@ -237,6 +242,7 @@ class decort_rg(DecortController):
|
||||
ret_dict['computes'] = self.rg_facts['vms']
|
||||
ret_dict['uniqPools'] = self.rg_facts['uniqPools']
|
||||
ret_dict['description'] = self.rg_facts['desc']
|
||||
ret_dict['sdn_access_group_id'] = self.rg_facts['sdn_access_group_id']
|
||||
|
||||
return ret_dict
|
||||
|
||||
@@ -334,11 +340,34 @@ class decort_rg(DecortController):
|
||||
recursive_deletion=dict(
|
||||
type='bool',
|
||||
default=False,
|
||||
)
|
||||
),
|
||||
sdn_access_group_id=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
|
||||
if (
|
||||
self.aparams['sdn_access_group_id'] is not None
|
||||
and (
|
||||
self.aparams['sdn_access_group_id']
|
||||
!= self.rg_facts['sdn_access_group_id']
|
||||
)
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "sdn_access_group_id" failed: '
|
||||
'cannot change sdn_access_group_id for an existing resource '
|
||||
f'group ID {self.validated_rg_id}.'
|
||||
)
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
# Workflow digest:
|
||||
# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECORTController
|
||||
# 2) check if the RG with the specified id or rg_name:name exists
|
||||
|
||||
@@ -4,7 +4,7 @@ DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_snapshot
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
import time
|
||||
@@ -17,33 +17,24 @@ class DecortSnapshot(DecortController):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
self.check_amodule_args()
|
||||
|
||||
self.vm_id: int
|
||||
self.vm_facts: dict
|
||||
self.aparams_label = self.aparams['label']
|
||||
self.aparams_vm_id = self.aparams['vm_id']
|
||||
|
||||
vm_id, vm_facts, _ = self._compute_get_by_id(
|
||||
self.vm_id, self.vm_facts, _ = self._compute_get_by_id(
|
||||
comp_id=self.aparams_vm_id,
|
||||
)
|
||||
if not vm_id:
|
||||
if not self.vm_id:
|
||||
self.message(f'VM {self.aparams_vm_id} not found')
|
||||
self.exit(fail=True)
|
||||
|
||||
self.vm_name = vm_facts['name']
|
||||
self.vm_snapshots = vm_facts['snapSets']
|
||||
self.vm_name = self.vm_facts['name']
|
||||
self.vm_snapshots = self.vm_facts['snapSets']
|
||||
self.vm_snapshot_labels = [
|
||||
snapshot['label'] for snapshot in self.vm_snapshots
|
||||
]
|
||||
|
||||
if (
|
||||
self.aparams_label is not None
|
||||
and self.aparams_label not in self.vm_snapshot_labels
|
||||
and self.aparams['state'] is None
|
||||
):
|
||||
self.message(
|
||||
f'Snapshot {self.aparams_label} '
|
||||
f'not found for VM {self.aparams_vm_id}'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
self.new_snapshot_label = None
|
||||
if self.aparams['state'] == 'present':
|
||||
if self.aparams_label is None:
|
||||
@@ -53,6 +44,17 @@ class DecortSnapshot(DecortController):
|
||||
elif self.aparams_label not in self.vm_snapshot_labels:
|
||||
self.new_snapshot_label = self.aparams_label
|
||||
|
||||
if (
|
||||
self.new_snapshot_label is None
|
||||
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)
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
@@ -65,6 +67,7 @@ class DecortSnapshot(DecortController):
|
||||
choices=(
|
||||
'absent',
|
||||
'present',
|
||||
'merge_aborted',
|
||||
),
|
||||
),
|
||||
usage=dict(
|
||||
@@ -101,6 +104,7 @@ class DecortSnapshot(DecortController):
|
||||
|
||||
def run(self):
|
||||
self.get_info(first_run=True)
|
||||
self.check_amodule_args_for_change()
|
||||
self.change()
|
||||
self.exit()
|
||||
|
||||
@@ -126,6 +130,8 @@ class DecortSnapshot(DecortController):
|
||||
case 'absent':
|
||||
if self.aparams_label in self.vm_snapshot_labels:
|
||||
self.delete()
|
||||
case 'merge_aborted':
|
||||
self.abort_merge()
|
||||
|
||||
def create(self):
|
||||
self.snapshot_create(
|
||||
@@ -141,6 +147,13 @@ class DecortSnapshot(DecortController):
|
||||
)
|
||||
self.facts = {}
|
||||
|
||||
def abort_merge(self):
|
||||
self.snapshot_abort_merge(
|
||||
vm_id=self.aparams_vm_id,
|
||||
label=self.aparams_label,
|
||||
)
|
||||
self.get_info()
|
||||
|
||||
def get_snapshot_usage(self) -> int:
|
||||
label = self.new_snapshot_label or self.aparams_label
|
||||
common_snapshots_usage_info, _ = self.snapshot_usage(
|
||||
@@ -148,6 +161,24 @@ class DecortSnapshot(DecortController):
|
||||
label=label,
|
||||
)
|
||||
return common_snapshots_usage_info['stored']
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
|
||||
if (
|
||||
self.aparams['state'] == 'merge_aborted'
|
||||
and self.vm_facts['techStatus'] != 'MERGE'
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "state" failed: '
|
||||
'Merge can be aborted only for VM in "MERGE" tech status.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
DecortSnapshot().run()
|
||||
|
||||
51
library/decort_trunk.py
Normal file
51
library/decort_trunk.py
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_trunk
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
|
||||
|
||||
class DecortTrunk(DecortController):
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
self.id: int = self.aparams['id']
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
id=dict(
|
||||
type='int',
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.get_info()
|
||||
self.exit()
|
||||
|
||||
def get_info(self):
|
||||
self.facts = self.trunk_get(id=self.id)
|
||||
self.facts['account_ids'] = self.facts.pop('accountIds')
|
||||
self.facts['created_timestamp'] = self.facts.pop('created_at')
|
||||
self.facts['deleted_timestamp'] = self.facts.pop('deleted_at')
|
||||
self.facts['updated_timestamp'] = self.facts.pop('updated_at')
|
||||
self.facts['native_vlan_id'] = self.facts.pop('nativeVlanId')
|
||||
self.facts['ovs_bridge'] = self.facts.pop('ovsBridge')
|
||||
self.facts['vlan_ids'] = self.facts.pop('trunkTags')
|
||||
|
||||
|
||||
def main():
|
||||
DecortTrunk().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -4,7 +4,7 @@ DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_user_info
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home).
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
@@ -33,9 +33,8 @@ class DecortUserInfo(DecortController):
|
||||
rights=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
e.value
|
||||
for e in self.AccountUserRights
|
||||
],
|
||||
e.value for e in self.AccountUserRights
|
||||
],
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
@@ -47,7 +46,7 @@ class DecortUserInfo(DecortController):
|
||||
type='str',
|
||||
choices=[
|
||||
e.value for e in self.AccountStatus
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -79,8 +78,8 @@ class DecortUserInfo(DecortController):
|
||||
type='str',
|
||||
choices=[
|
||||
e.value
|
||||
for e in self.AccountSortableField
|
||||
],
|
||||
for e in self.AccountSortableField
|
||||
],
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
@@ -186,6 +185,124 @@ class DecortUserInfo(DecortController):
|
||||
type='bool',
|
||||
default=False,
|
||||
),
|
||||
zones=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
filter=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
deletable=dict(
|
||||
type='bool',
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
),
|
||||
grid_id=dict(
|
||||
type='int',
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
),
|
||||
name=dict(
|
||||
type='str',
|
||||
),
|
||||
node_id=dict(
|
||||
type='int',
|
||||
),
|
||||
status=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
e.value for e in self.ZoneStatus
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
pagination=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
number=dict(
|
||||
type='int',
|
||||
default=1,
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
),
|
||||
),
|
||||
),
|
||||
sorting=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
asc=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=self.ZoneField._member_names_,
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
trunks=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
filter=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
ids=dict(
|
||||
type='list',
|
||||
),
|
||||
account_ids=dict(
|
||||
type='list',
|
||||
),
|
||||
status=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
e.value for e in self.TrunkStatus
|
||||
],
|
||||
),
|
||||
vlan_ids=dict(
|
||||
type='list',
|
||||
),
|
||||
),
|
||||
),
|
||||
pagination=dict(
|
||||
type='dict',
|
||||
apply_defaults=True,
|
||||
options=dict(
|
||||
number=dict(
|
||||
type='int',
|
||||
default=1,
|
||||
),
|
||||
size=dict(
|
||||
type='int',
|
||||
default=50,
|
||||
),
|
||||
),
|
||||
),
|
||||
sorting=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
asc=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
e.value
|
||||
for e in self.TrunksSortableField
|
||||
],
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
@@ -200,22 +317,38 @@ class DecortUserInfo(DecortController):
|
||||
check_error = False
|
||||
|
||||
match self.aparams['audits']:
|
||||
case {'filter': {'time':
|
||||
{'start': {'datetime': str() as dt_str}}
|
||||
}
|
||||
}:
|
||||
case {
|
||||
'filter': {'time': {'start': {'datetime': str() as dt_str}}}
|
||||
}:
|
||||
if self.dt_str_to_sec(dt_str=dt_str) is None:
|
||||
self.message(self.MESSAGES.str_not_parsed(string=dt_str))
|
||||
check_error = True
|
||||
match self.aparams['audits']:
|
||||
case {'filter': {'time':
|
||||
{'end': {'datetime': str() as dt_str}}
|
||||
}
|
||||
}:
|
||||
case {
|
||||
'filter': {'time': {'end': {'datetime': str() as dt_str}}}
|
||||
}:
|
||||
if self.dt_str_to_sec(dt_str=dt_str) is None:
|
||||
self.message(self.MESSAGES.str_not_parsed(string=dt_str))
|
||||
check_error = True
|
||||
|
||||
aparam_trunks = self.aparams['trunks']
|
||||
if (
|
||||
aparam_trunks is not None
|
||||
and aparam_trunks['filter'] is not None
|
||||
and aparam_trunks['filter']['vlan_ids'] is not None
|
||||
):
|
||||
for vlan_id in aparam_trunks['filter']['vlan_ids']:
|
||||
if not (
|
||||
self.TRUNK_VLAN_ID_MIN_VALUE
|
||||
<= vlan_id
|
||||
<= self.TRUNK_VLAN_ID_MAX_VALUE
|
||||
):
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "trunks.filter.vlan_ids" failed: '
|
||||
f'VLAN ID {vlan_id} must be in range 1-4095.'
|
||||
)
|
||||
|
||||
if check_error:
|
||||
self.exit(fail=True)
|
||||
|
||||
@@ -331,6 +464,94 @@ class DecortUserInfo(DecortController):
|
||||
|
||||
return mapped_args
|
||||
|
||||
@property
|
||||
def mapped_zones_args(self):
|
||||
"""
|
||||
Map the module argument `zones` to
|
||||
arguments dictionary for the method
|
||||
`DecortController.user_zones`.
|
||||
"""
|
||||
|
||||
input_args = self.aparams['zones']
|
||||
if not input_args:
|
||||
return input_args
|
||||
|
||||
mapped_args = {}
|
||||
|
||||
input_args_filter = input_args['filter']
|
||||
if input_args_filter:
|
||||
mapped_args.update(input_args_filter)
|
||||
|
||||
input_args_filter_status = input_args_filter['status']
|
||||
if input_args_filter_status:
|
||||
mapped_args['status'] = (
|
||||
self.ZoneStatus(input_args_filter_status)
|
||||
)
|
||||
|
||||
input_args_pagination = input_args['pagination']
|
||||
if input_args_pagination:
|
||||
mapped_args['page_number'] = input_args_pagination['number']
|
||||
mapped_args['page_size'] = input_args_pagination['size']
|
||||
|
||||
input_args_sorting = input_args['sorting']
|
||||
if input_args_sorting:
|
||||
mapped_args['sort_by_asc'] = input_args_sorting['asc']
|
||||
|
||||
input_args_sorting_field = input_args_sorting['field']
|
||||
if input_args_sorting_field:
|
||||
mapped_args['sort_by_field'] = (
|
||||
self.ZoneField._member_map_[input_args_sorting_field]
|
||||
)
|
||||
|
||||
return mapped_args
|
||||
|
||||
@property
|
||||
def mapped_trunks_args(self):
|
||||
"""
|
||||
Map the module argument `trunks` to
|
||||
arguments dictionary for the method
|
||||
`DecortController.user_trunks`.
|
||||
"""
|
||||
|
||||
input_args = self.aparams['trunks']
|
||||
if not input_args:
|
||||
return input_args
|
||||
|
||||
mapped_args = {}
|
||||
|
||||
input_args_filter = input_args['filter']
|
||||
if input_args_filter:
|
||||
mapped_args.update(input_args_filter)
|
||||
|
||||
input_args_filter_status = input_args_filter['status']
|
||||
if input_args_filter_status:
|
||||
mapped_args['status'] = (
|
||||
self.TrunkStatus(input_args_filter_status)
|
||||
)
|
||||
|
||||
input_args_filter_vlan_ids = input_args_filter['vlan_ids']
|
||||
if input_args_filter_vlan_ids is not None:
|
||||
mapped_args['vlan_ids'] = ', '.join(
|
||||
map(str, input_args_filter_vlan_ids)
|
||||
)
|
||||
|
||||
input_args_pagination = input_args['pagination']
|
||||
if input_args_pagination:
|
||||
mapped_args['page_number'] = input_args_pagination['number']
|
||||
mapped_args['page_size'] = input_args_pagination['size']
|
||||
|
||||
input_args_sorting = input_args['sorting']
|
||||
if input_args_sorting:
|
||||
mapped_args['sort_by_asc'] = input_args_sorting['asc']
|
||||
|
||||
input_args_sorting_field = input_args_sorting['field']
|
||||
if input_args_sorting_field:
|
||||
mapped_args['sort_by_field'] = (
|
||||
self.TrunksSortableField(input_args_sorting_field)
|
||||
)
|
||||
|
||||
return mapped_args
|
||||
|
||||
def run(self):
|
||||
self.get_info()
|
||||
self.exit()
|
||||
@@ -357,13 +578,34 @@ class DecortUserInfo(DecortController):
|
||||
if self.aparams['api_methods']:
|
||||
self.facts['api_methods'] = self.user_api_methods(id=self.id)
|
||||
|
||||
|
||||
search_string = self.aparams['objects_search']
|
||||
if search_string:
|
||||
self.facts['objects_search'] = self.user_objects_search(
|
||||
search_string=search_string,
|
||||
)
|
||||
|
||||
if self.aparams['zones']:
|
||||
self.facts['zones'] = self.user_zones(**self.mapped_zones_args)
|
||||
|
||||
if self.aparams['trunks']:
|
||||
self.facts['trunks'] = self.user_trunks(**self.mapped_trunks_args)
|
||||
for trunk_facts in self.facts['trunks']:
|
||||
trunk_facts['account_ids'] = trunk_facts.pop('accountIds')
|
||||
trunk_facts['created_timestamp'] = trunk_facts.pop(
|
||||
'created_at'
|
||||
)
|
||||
trunk_facts['deleted_timestamp'] = trunk_facts.pop(
|
||||
'deleted_at'
|
||||
)
|
||||
trunk_facts['updated_timestamp'] = trunk_facts.pop(
|
||||
'updated_at'
|
||||
)
|
||||
trunk_facts['native_vlan_id'] = trunk_facts.pop(
|
||||
'nativeVlanId'
|
||||
)
|
||||
trunk_facts['ovs_bridge'] = trunk_facts.pop('ovsBridge')
|
||||
trunk_facts['vlan_ids'] = trunk_facts.pop('trunkTags')
|
||||
|
||||
|
||||
def main():
|
||||
DecortUserInfo().run()
|
||||
|
||||
@@ -24,7 +24,6 @@ class decort_vins(DecortController):
|
||||
validated_rg_id = 0
|
||||
rg_facts = None # will hold RG facts
|
||||
validated_acc_id = 0
|
||||
acc_facts = None # will hold Account facts
|
||||
|
||||
if arg_amodule.params['vins_id']:
|
||||
# expect existing ViNS with the specified ID
|
||||
@@ -45,6 +44,7 @@ class decort_vins(DecortController):
|
||||
# This call to rg_find will abort the module if no RG with such ID is present
|
||||
validated_rg_id, rg_facts = self.rg_find(0, # account ID set to 0 as we search for RG by RG ID
|
||||
arg_amodule.params['rg_id'], arg_rg_name="")
|
||||
validated_acc_id = rg_facts['accountId']
|
||||
|
||||
# This call to vins_find may return vins_id=0 if no ViNS found
|
||||
self.vins_id, self.vins_facts = self.vins_find(vins_id=0, vins_name=arg_amodule.params['vins_name'],
|
||||
@@ -56,7 +56,7 @@ class decort_vins(DecortController):
|
||||
pass
|
||||
elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "":
|
||||
# Specified account must be present and accessible by the user, otherwise abort the module
|
||||
validated_acc_id, acc_facts = self.account_find(arg_amodule.params['account_name'], arg_amodule.params['account_id'])
|
||||
validated_acc_id, self._acc_info = self.account_find(arg_amodule.params['account_name'], arg_amodule.params['account_id'])
|
||||
if not validated_acc_id:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = ("Current user does not have access to the requested account "
|
||||
@@ -102,15 +102,25 @@ class decort_vins(DecortController):
|
||||
self.amodule.fail_json(**self.result)
|
||||
|
||||
return
|
||||
|
||||
self.rg_id = validated_rg_id
|
||||
self.acc_id = validated_acc_id
|
||||
|
||||
if self.vins_id and self.vins_facts['status'] != 'DESTROYED':
|
||||
self.check_amodule_args_for_change()
|
||||
else:
|
||||
self.check_amodule_args_for_create()
|
||||
|
||||
return
|
||||
|
||||
def create(self):
|
||||
self.vins_id = self.vins_provision(self.amodule.params['vins_name'],
|
||||
self.acc_id, self.rg_id,
|
||||
self.amodule.params['ipcidr'],
|
||||
self.amodule.params['ext_net_id'], self.amodule.params['ext_ip_addr'],
|
||||
self.amodule.params['description'])
|
||||
self.amodule.params['description'],
|
||||
zone_id=self.amodule.params['zone_id'],
|
||||
)
|
||||
|
||||
if self.amodule.params['mgmtaddr'] or self.amodule.params['connect_to']:
|
||||
_, self.vins_facts = self.vins_find(self.vins_id)
|
||||
@@ -150,6 +160,14 @@ class decort_vins(DecortController):
|
||||
|
||||
if d_state != '':
|
||||
self.vins_state(self.vins_facts, d_state)
|
||||
|
||||
aparam_zone_id = self.aparams['zone_id']
|
||||
if aparam_zone_id is not None and aparam_zone_id != self.vins_facts['zoneId']:
|
||||
self.vins_migrate_to_zone(
|
||||
net_id=self.vins_id,
|
||||
zone_id=aparam_zone_id,
|
||||
)
|
||||
|
||||
return
|
||||
def delete(self):
|
||||
self.vins_delete(self.vins_id, permanently=True)
|
||||
@@ -235,6 +253,7 @@ class decort_vins(DecortController):
|
||||
else:
|
||||
ret_dict['ext_ip_addr'] = ""
|
||||
ret_dict['ext_net_id'] = -1
|
||||
ret_dict['zone_id'] = self.vins_facts['zoneId']
|
||||
|
||||
return ret_dict
|
||||
|
||||
@@ -309,12 +328,31 @@ class decort_vins(DecortController):
|
||||
type='str',
|
||||
default='',
|
||||
),
|
||||
zone_id=dict(
|
||||
type=int,
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
('vins_id', 'vins_name'),
|
||||
],
|
||||
)
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
if self.check_aparam_zone_id() is False:
|
||||
check_errors = True
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
# Workflow digest:
|
||||
# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECORTController
|
||||
|
||||
48
library/decort_zone.py
Normal file
48
library/decort_zone.py
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: decort_zone
|
||||
|
||||
description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). # noqa: E501
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.decort_utils import DecortController
|
||||
|
||||
|
||||
class DecortZone(DecortController):
|
||||
def __init__(self):
|
||||
super().__init__(AnsibleModule(**self.amodule_init_args))
|
||||
self.id: int = self.aparams['id']
|
||||
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
id=dict(
|
||||
type='int',
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.get_info()
|
||||
self.exit()
|
||||
|
||||
def get_info(self):
|
||||
self.facts = self.zone_get(id=self.id)
|
||||
self.facts['grid_id'] = self.facts.pop('gid')
|
||||
self.facts['created_timestamp'] = self.facts.pop('createdTime')
|
||||
self.facts['updated_timestamp'] = self.facts.pop('updatedTime')
|
||||
self.facts['node_ids'] = self.facts.pop('nodeIds')
|
||||
|
||||
|
||||
def main():
|
||||
DecortZone().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user