This commit is contained in:
2025-03-03 17:44:25 +03:00
parent e537eadda6
commit f8c32d609b
27 changed files with 10248 additions and 196 deletions

View File

@@ -1130,81 +1130,77 @@ class DecortController(object):
break
return
def compute_data_disks(self, comp_dict, new_data_disks):
"""Manage attachment of data disks to the Compute instance.
@waypoint
@checkmode
def compute_disks(self, comp_dict: dict, aparam_disks: dict):
disks = set(aparam_disks['ids'])
mode = aparam_disks['mode']
@param (dict) comp_dict: dictionary with Compute facts, that identifies the Compute instance
to manage data disks for.
@param (list of int) new_data_disks: list of integer IDs for the disks that must be attached to
this Compute instance. If some disk IDs appear in this list, but are not present in comp_dict,
these disks will be attached. Vice versa, if some disks appear in comp_dict but are not present
in data_disks, such disks will be detached.
comp_disks = {disk['id'] for disk in comp_dict['disks']}
disks_to_attach = set()
disks_to_detach = set()
disks_to_delete = set()
match mode:
case 'update':
disks_to_attach = disks - comp_disks
case 'detach':
disks_to_detach = disks & comp_disks
case 'delete':
disks_to_delete = disks & comp_disks
case 'match':
disks_to_attach = disks - comp_disks
disks_to_detach = comp_disks - disks
Note:
1) you cannot control boot disk attachment, so including this Compute's boot disk ID
into data_disk list will have no effect (as well as not listing it there).
2) this function may modify data_disks by removing from it an ID that corresponds to
this Compute's boot disk (if found).
3) In view of #2, upstream code may need to reread compute facts (com_dict) so that it
contains updated information about attached disks.
"""
for disk_id in disks_to_attach:
api_params = {
'computeId': comp_dict['id'],
'diskId': disk_id,
'diskType': 'D',
}
self.decort_api_call(
arg_req_function=requests.post,
arg_api_name='/restmachine/cloudapi/compute/diskAttach',
arg_params=api_params,
)
self.set_changed()
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "compute_data_disks")
for disk_id 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.set_changed()
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = ("compute_data_disks() in check mode: managing data disks on Compute "
"ID {} was requested.").format(comp_dict['id'])
return
for disk_id 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.set_changed()
bdisk_id = 0
current_list = []
detach_list = []
attach_list = []
# 'data_disks' argument for decort_kvmvm module expects list of integer disk IDs.
# However, if the value for disk ID comes via Jinja templating like this:
# data_disks:
# - "{{ my_disk.facts.id }}" <= will come as string, which is WRONG!!!
# - 4571 <= no Jinja templae, will come as int - OK
#
# then all values when entering this method will be of type string. We need to
# explicitly cast int type on all of them.
if new_data_disks is not None:
for idx, repair in enumerate(new_data_disks):
new_data_disks[idx] = int(repair)
else:
new_data_disks = []
for disk in comp_dict['disks']:
if disk['type'] == 'B':
bdisk_id = disk['id']
if bdisk_id in new_data_disks:
# If boot disk ID is listed in data_disks - remove it
new_data_disks.remove(bdisk_id)
elif disk['type'] == 'D':
# build manipulation sets for 'D' type disks only
current_list.append(disk['id'])
if disk['id'] not in new_data_disks:
detach_list.append(disk['id'])
attach_list = [did for did in new_data_disks if did not in current_list]
for did in detach_list:
api_params = dict(computeId=comp_dict['id'], diskId=did)
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/diskDetach", api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json.
self.result['changed'] = True
for did in attach_list:
api_params = dict(computeId=comp_dict['id'], diskId=did)
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/diskAttach", api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json.
self.result['changed'] = True
self.result['failed'] = False
return
@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_delete(self, comp_id, permanently=False,detach=True):
"""Delete a Compute instance identified by its ID. It is assumed that the Compute with the specified
@@ -1233,7 +1229,12 @@ class DecortController(object):
return
def _compute_get_by_id(self, comp_id, need_custom_fields: bool = False):
def _compute_get_by_id(
self,
comp_id,
need_custom_fields: bool = False,
need_console_url: bool = False,
):
"""Helper function that locates compute instance by ID and returns Compute facts.
@param (int) comp_id: ID of the Compute instance to find and return facts for.
@@ -1266,16 +1267,23 @@ 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,
)
ret_comp_dict['console_url'] = console_url
else:
self.result['warning'] = ("compute_get_by_id(): failed to get Compute by ID {}. HTTP code {}, "
"response {}.").format(comp_id, api_resp.status_code, api_resp.reason)
return ret_comp_id, ret_comp_dict, ret_rg_id
def compute_find(self, comp_id,
def compute_find(self, comp_id: int = 0,
comp_name="", rg_id=0,
check_state=True,
need_custom_fields: bool = False):
need_custom_fields: bool = False,
need_console_url: bool = False):
"""Tries to find Compute instance according to the specified parameters. On success returns non-zero
Compute ID and a dictionary with Compute details, or 0 for ID and None for the dictionary on failure.
@@ -1312,6 +1320,7 @@ class DecortController(object):
self._compute_get_by_id(
comp_id=comp_id,
need_custom_fields=need_custom_fields,
need_console_url=need_console_url,
)
)
if not ret_comp_id:
@@ -1352,6 +1361,7 @@ class DecortController(object):
_, ret_comp_dict, _ = self._compute_get_by_id(
comp_id=ret_comp_id,
need_custom_fields=need_custom_fields,
need_console_url=need_console_url,
)
break
@@ -1436,7 +1446,7 @@ class DecortController(object):
def kvmvm_provision(self, rg_id,
comp_name,
cpu, ram,
boot_disk,
boot_disk_size,
image_id,
chipset: Literal['Q35', 'i440fx'] = 'i440fx',
description="",
@@ -1478,12 +1488,12 @@ class DecortController(object):
'name': comp_name,
'cpu': cpu,
'ram': ram,
'bootDisk': boot_disk,
'bootDisk': boot_disk_size,
'sepId': sep_id,
'pool': pool_name,
'interfaces': '[]', # we create VM without any network connections
'chipset': chipset,
'withoutBootDisk': not boot_disk,
'withoutBootDisk': not boot_disk_size,
'preferredCpu': preferred_cpu_cores,
}
if description:
@@ -2096,6 +2106,48 @@ class DecortController(object):
)
self.set_changed()
@waypoint
@checkmode
def compute_clone(
self,
compute_id: int,
name: str,
snapshot_timestamp: int | None = None,
snapshot_name: str | None = None,
snapshot_datetime: str | None = None,
force: bool = False,
):
_snapshot_timestamp = snapshot_timestamp
if snapshot_datetime:
_snapshot_timestamp = self.dt_str_to_sec(dt_str=snapshot_datetime)
api_params = {
'computeId': compute_id,
'name': name,
'force': force,
'snapshotName': snapshot_name,
'snapshotTimestamp': _snapshot_timestamp,
}
api_resp = self.decort_api_call(
arg_req_function=requests.post,
arg_api_name='/restmachine/cloudapi/compute/clone',
arg_params=api_params,
)
self.set_changed()
return int(api_resp.content)
@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
###################################
# OS image manipulation methods
###################################
@@ -2616,7 +2668,7 @@ class DecortController(object):
# TODO: this method will not work in its current implementation. Update it for new .../rg/update specs.
def rg_update(self, arg_rg_dict, arg_quotas, arg_res_types, arg_newname, arg_sep_pools):
def rg_update(self, arg_rg_dict, arg_quotas, arg_res_types, arg_newname, arg_sep_pools, arg_desc: str | None = None):
"""Manage quotas for an existing RG.
@param arg_rg_dict: dictionary with RG facts as returned by rg_find(...) method or .../rg/get API
@@ -2697,6 +2749,10 @@ class DecortController(object):
api_params['clearUniqPools'] = True
update_required = True
if arg_desc is not None and arg_desc != arg_rg_dict['desc']:
api_params['desc'] = arg_desc
update_required = True
if update_required:
self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/update", api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json.
@@ -4518,7 +4574,7 @@ class DecortController(object):
return 0, None
def disk_create(self, accountId, name, description, size, type, iops, sep_id, pool):
def disk_create(self, accountId, name, description, size, iops, sep_id, pool):
"""Provision Disk according to the specified arguments.
Note that disks created by this method will be of type 'D' (data disks).
If critical error occurs the embedded call to API function will abort further execution
@@ -4542,7 +4598,6 @@ class DecortController(object):
name=name,
description=description,
size=size,
type=type,
iops=iops,
sep_id=sep_id,
pool=pool )
@@ -5140,6 +5195,7 @@ class DecortController(object):
description,
extnet_only,
master_chipset: Literal['Q35', 'i440fx'] = 'i440fx',
lb_sysctl: dict | None = None,
):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_provision")
@@ -5185,6 +5241,9 @@ class DecortController(object):
userData=json.dumps(default_worker['ci_user_data']),
extnetOnly=extnet_only,
chipset=master_chipset,
lbSysctlParams=lb_sysctl and json.dumps(
{k: str(v) for k, v in lb_sysctl.items()}
),
)
upload_files = None
@@ -5903,7 +5962,7 @@ class DecortController(object):
self.amodule.fail_json(**self.result)
return 0, None
def lb_provision(self,lb_name,rg_id,vins_id,ext_net_id,ha_status,description,start=True):
def lb_provision(self,lb_name,rg_id,vins_id,ext_net_id,ha_status,description,start=True, sysctl: dict | None = None):
"""Provision LB according to the specified arguments.
If critical error occurs the embedded call to API function will abort further execution of
the script and relay error to Ansible.
@@ -5932,7 +5991,10 @@ class DecortController(object):
vinsId=vins_id,
highlyAvailable=ha_status,
start=start,
desc=description
desc=description,
sysctlParams=sysctl and json.dumps(
{k: str(v) for k, v in sysctl.items()}
),
)
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.
@@ -6259,8 +6321,15 @@ class DecortController(object):
api_resp = self.decort_api_call(requests.post, api_url, api_params)
self.result['changed'] = True
def lb_update(self,prime,front_ha_ip,back_ha_ip,lb_backends=[],lb_frontends=[],mod_backends=[],mod_servers=[],mod_frontends=[]):
def lb_update(
self,
lb_facts: dict,
aparam_backends: list | None,
aparam_frontends: list | None,
aparam_servers: list | None,
aparam_sysctl: dict | None,
):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "lb_update")
if self.amodule.check_mode:
@@ -6271,11 +6340,13 @@ class DecortController(object):
self.result['msg'] = result_msg
return
if mod_backends is None:
lb_backends = lb_facts['backends']
lb_frontends = lb_facts['frontends']
if aparam_backends is None:
upd_back_list = [back['name'] for back in lb_backends]
else:
#lists from module and cloud
mod_backs_list = [back['name'] for back in mod_backends]
mod_backs_list = [back['name'] for back in aparam_backends]
lb_backs_list = [back['name'] for back in lb_backends]
#ADD\DEL\UPDATE LISTS OF BACKENDS
del_list_backs = set(lb_backs_list).difference(mod_backs_list)
@@ -6294,21 +6365,21 @@ class DecortController(object):
if add_back_list:
self._lb_create_backends(
add_back_list,
mod_backends,
mod_servers
aparam_backends,
aparam_servers
)
if upd_back_list:
if mod_backends is not None or mod_servers is not None:
if aparam_backends is not None or aparam_servers is not None:
self._lb_update_backends(
upd_back_list,
lb_backends,
mod_backends,
mod_servers,
aparam_backends,
aparam_servers,
)
if mod_frontends is not None:
mod_front_list = [front['name'] for front in mod_frontends]
if aparam_frontends is not None:
mod_front_list = [front['name'] for front in aparam_frontends]
lb_front_list = [front['name'] for front in lb_frontends]
del_list_fronts = set(lb_front_list).difference(mod_front_list)
@@ -6318,6 +6389,8 @@ class DecortController(object):
if del_list_fronts:
self._lb_delete_fronts(del_list_fronts)
front_ha_ip = lb_facts['frontendHAIP']
back_ha_ip = lb_facts['backendHAIP']
#set bind_ip
if front_ha_ip != "":
bind_ip = front_ha_ip
@@ -6326,16 +6399,25 @@ class DecortController(object):
bind_ip = back_ha_ip
if front_ha_ip == "" and back_ha_ip == "":
prime = lb_facts['primaryNode']
if prime["frontendIp"] != "":
bind_ip = prime["frontendIp"]
else:
bind_ip = prime["backendIp"]
if add_list_fronts:
self._lb_add_fronts(add_list_fronts,mod_frontends,bind_ip)
self._lb_add_fronts(add_list_fronts,aparam_frontends,bind_ip)
if upd_front_list:
self._lb_update_fronts(upd_front_list,lb_frontends,mod_frontends,bind_ip)
self._lb_update_fronts(upd_front_list,lb_frontends,aparam_frontends,bind_ip)
if aparam_sysctl is not None:
sysctl_with_str_values = {k: str(v) for k, v in aparam_sysctl.items()}
if sysctl_with_str_values != lb_facts['sysctlParams']:
self.lb_update_sysctl(
lb_id=lb_facts['id'],
sysctl=aparam_sysctl,
)
return
def _lb_delete_backends(self,back_list,lb_fronts):
@@ -6556,6 +6638,22 @@ class DecortController(object):
return
@waypoint
@checkmode
def lb_update_sysctl(self, lb_id: int, sysctl: dict):
sysctl_with_str_values = json.dumps(
{k: str(v) for k, v in sysctl.items()}
)
self.decort_api_call(
arg_req_function=requests.post,
arg_api_name='/restmachine/cloudapi/lb/updateSysctlParams',
arg_params={
'lbId': lb_id,
'sysctlParams': sysctl_with_str_values,
},
)
self.set_changed()
@waypoint
@checkmode
def snapshot_create(self, compute_id: int, label: str):