This commit is contained in:
2025-02-07 13:04:30 +03:00
parent 5f3df12742
commit e537eadda6
22 changed files with 9623 additions and 84 deletions

View File

@@ -18,6 +18,8 @@ class decort_kvmvm(DecortController):
super(decort_kvmvm, self).__init__(AnsibleModule(**self.amodule_init_args))
arg_amodule = self.amodule
self.aparam_networks_has_dpdk = None
self.check_amodule_args()
self.comp_should_exist = False
@@ -30,6 +32,7 @@ class decort_kvmvm(DecortController):
self.rg_id = 0
self.aparam_image = None
validated_acc_id =0
validated_rg_id = 0
validated_rg_facts = None
@@ -84,38 +87,9 @@ class decort_kvmvm(DecortController):
if self.comp_id:
self.comp_should_exist = True
self.acc_id = self.comp_info['accountId']
check_error = False
params_to_check = {
'chipset': 'chipset',
'cpu_pin': 'cpupin',
'hp_backed': 'hpBacked',
'numa_affinity': 'numaAffinity',
}
for param_name, comp_field_name in params_to_check.items():
if (
self.aparams[param_name] is not None
and self.comp_info[comp_field_name] != self.aparams[param_name]
and self.aparams['state'] not in ('halted', 'poweredoff')
):
self.message(
f'Cannot change "{param_name}" for compute '
f'{self.comp_id} if parameter "state" is not '
f'halted or poweredoff.'
)
check_error = True
if check_error:
self.exit(fail=True)
else:
if self.aparams['chipset'] is None:
self.message(
'Check for parameter "chipset" failed: '
'chipset must be specified for a new compute.'
)
self.exit(fail=True)
if self.amodule.params['state'] != 'absent':
self.check_amodule_args_for_create()
return
def check_amodule_args(self):
@@ -124,13 +98,16 @@ class decort_kvmvm(DecortController):
cannot be implemented using Ansible Argument spec.
"""
check_error = False
# Check parameter "networks"
aparam_nets = self.aparams['networks']
if aparam_nets:
check_error = False
net_types = {net['type'] for net in aparam_nets}
# DPDK and other networks
self.aparam_networks_has_dpdk = False
if self.VMNetType.DPDK.value in net_types:
self.aparam_networks_has_dpdk = True
if not net_types.issubset(
{self.VMNetType.DPDK.value, self.VMNetType.EMPTY.value}
):
@@ -140,6 +117,17 @@ class decort_kvmvm(DecortController):
' a compute cannot be connected to a DPDK network and'
' a network of another type at the same time.'
)
if (
self.aparams['hp_backed'] is not None
and not self.aparams['hp_backed']
):
check_error = True
self.message(
'Check for parameter "networks" failed: '
'hp_backed must be set to True to connect a compute '
'to a DPDK network.'
)
# MTU for non-DPDK networks
for net in aparam_nets:
if (
@@ -153,8 +141,6 @@ class decort_kvmvm(DecortController):
' (remove parameter "mtu" for network'
f' {net["type"]} with ID {net["id"]}).'
)
if check_error:
self.exit(fail=True)
aparam_custom_fields = self.aparams['custom_fields']
if aparam_custom_fields is not None:
@@ -162,11 +148,25 @@ class decort_kvmvm(DecortController):
aparam_custom_fields['disable']
and aparam_custom_fields['fields'] is not None
):
check_error = True
self.message(
'Check for parameter "custom_fields" failed: '
'"fields" cannot be set if "disable" is True.'
)
self.exit(fail=True)
aparam_pref_cpu_cores = self.aparams['preferred_cpu_cores']
if (
aparam_pref_cpu_cores
and len(set(aparam_pref_cpu_cores)) != len(aparam_pref_cpu_cores)
):
check_error = True
self.message(
'Check for parameter "preferred_cpu_cores" failed: '
'the list must contain only unique elements.'
)
if check_error:
self.exit(fail=True)
def nop(self):
"""No operation (NOP) handler for Compute management by decort_kvmvm module.
@@ -280,6 +280,15 @@ class decort_kvmvm(DecortController):
if numa_affinity is None:
numa_affinity = 'none'
chipset = self.amodule.params['chipset']
if chipset is None:
chipset = 'i440fx'
self.message(
msg=f'Chipset not specified, '
f'default value "{chipset}" will be used.',
warning=True,
)
# if we get through here, all parameters required to create new Compute instance should be at hand
# NOTE: KVM VM is created in HALTED state and must be explicitly started
@@ -293,10 +302,11 @@ class decort_kvmvm(DecortController):
sep_id=self.amodule.params['sep_id' ] if "sep_id" in self.amodule.params else None,
pool_name=self.amodule.params['pool'] if "pool" in self.amodule.params else None,
start_on_create=start_compute,
chipset=self.amodule.params['chipset'],
chipset=chipset,
cpu_pin=cpu_pin,
hp_backed=hp_backed,
numa_affinity=numa_affinity)
numa_affinity=numa_affinity,
preferred_cpu_cores=self.amodule.params['preferred_cpu_cores'],)
self.comp_should_exist = True
# Originally we would have had to re-read comp_info after VM was provisioned
@@ -406,6 +416,18 @@ class decort_kvmvm(DecortController):
Note that it does not modify power state of KVM VM.
"""
if self.compute_update_args:
self.compute_update(
compute_id=self.comp_info['id'],
**self.compute_update_args,
)
if self.amodule.params['rollback_to'] is not None:
self.compute_rollback(
compute_id=self.comp_info['id'],
snapshot_label=self.amodule.params['rollback_to'],
)
if self.amodule.params['networks'] is not None:
self.compute_networks(
comp_dict=self.comp_info,
@@ -430,12 +452,6 @@ class decort_kvmvm(DecortController):
self.amodule.params['aaff_rule'],
label=self.amodule.params['affinity_label'])
if self.compute_update_args:
self.compute_update(
compute_id=self.comp_info['id'],
**self.compute_update_args,
)
aparam_custom_fields = self.amodule.params['custom_fields']
if aparam_custom_fields is not None:
compute_custom_fields = self.comp_info['custom_fields']
@@ -465,14 +481,15 @@ class decort_kvmvm(DecortController):
'numa_affinity': 'numaAffinity',
'description': 'desc',
'auto_start': 'autoStart',
'preferred_cpu_cores': 'preferredCpu',
}
for param_name, comp_field_name in params_to_check.items():
aparam_value = self.amodule.params[param_name]
for aparam_name, comp_field_name in params_to_check.items():
aparam_value = self.amodule.params[aparam_name]
if (
aparam_value is not None
and aparam_value != self.comp_info[comp_field_name]
):
result_args[param_name] = aparam_value
result_args[aparam_name] = aparam_value
return result_args
@@ -511,6 +528,8 @@ class decort_kvmvm(DecortController):
numa_affinity="",
custom_fields={},
vnc_password="",
snapshots=[],
preferred_cpu_cores=[],
)
if check_mode or self.comp_info is None:
@@ -577,6 +596,10 @@ class decort_kvmvm(DecortController):
ret_dict['auto_start'] = self.comp_info['autoStart']
ret_dict['snapshots'] = self.comp_info['snapSets']
ret_dict['preferred_cpu_cores'] = self.comp_info['preferredCpu']
return ret_dict
def check_amodule_args_for_create(self):
@@ -627,6 +650,21 @@ class decort_kvmvm(DecortController):
)
self.exit(fail=True)
if self.aparams['rollback_to'] is not None:
self.message(
'Check for parameter "rollback_to" failed: '
'rollback_to can be specified only for existing compute.'
)
self.exit(fail=True)
if self.aparam_networks_has_dpdk and not self.aparams['hp_backed']:
self.message(
'Check for parameter "networks" failed:'
' hp_backed must be set to True to connect a compute'
' to a DPDK network.'
)
self.exit(fail=True)
@property
def amodule_init_args(self) -> dict:
return self.pack_amodule_init_args(
@@ -790,7 +828,14 @@ class decort_kvmvm(DecortController):
),
auto_start=dict(
type='bool',
)
),
rollback_to=dict(
type='str',
),
preferred_cpu_cores=dict(
type='list',
elements='int',
),
),
supports_check_mode=True,
required_one_of=[
@@ -799,6 +844,8 @@ class decort_kvmvm(DecortController):
)
def check_amodule_args_for_change(self):
check_errors = False
new_boot_disk_size = self.amodule.params['boot_disk']
if new_boot_disk_size is not None:
for disk in self.comp_info['disks']:
@@ -806,30 +853,109 @@ class decort_kvmvm(DecortController):
boot_disk_size = disk['sizeMax']
break
else:
check_errors = True
self.message(
f'Can\'t set boot disk size for Compute '
f'{self.comp_info["id"]}, because it doesn\'t have a '
f'boot disk.'
)
self.exit(fail=True)
if new_boot_disk_size < boot_disk_size:
check_errors = True
self.message(
f'New boot disk size {new_boot_disk_size} is less than '
f'current {boot_disk_size} for Compute ID '
f'{self.comp_info["id"]}'
)
self.exit(fail=True)
if (
not self.comp_info['imageId']
and self.amodule.params['state'] in ('poweredon', 'paused')
):
check_errors = True
self.message(
'Check for parameter "state" failed: '
'state for a blank Compute can not be "poweredon" or "paused".'
)
self.exit(fail=True)
is_vm_stopped_or_will_be_stopped = (
(
self.comp_info['techStatus'] == 'STOPPED'
and (
self.amodule.params['state'] is None
or self.amodule.params['state'] in (
'halted', 'poweredoff', 'present',
)
)
)
or (
self.comp_info['techStatus'] != 'STOPPED'
and self.amodule.params['state'] in (
'halted', 'poweredoff',
)
)
)
if self.amodule.params['rollback_to'] is not None:
if not is_vm_stopped_or_will_be_stopped:
check_errors = True
self.message(
'Check for parameter "rollback_to" failed: '
'VM must be stopped to rollback.'
)
vm_snapshot_labels = [
snapshot['label'] for snapshot in self.comp_info['snapSets']
]
if self.amodule.params['rollback_to'] not in vm_snapshot_labels:
check_errors = True
self.message(
f'Check for parameter "rollback_to" failed: '
f'snapshot with label '
f'{self.amodule.params["rollback_to"]} does not exist '
f'for VM ID{self.comp_info["id"]}.'
)
params_to_check = {
'chipset': 'chipset',
'cpu_pin': 'cpupin',
'hp_backed': 'hpBacked',
'numa_affinity': 'numaAffinity',
}
for param_name, comp_field_name in params_to_check.items():
if (
self.aparams[param_name] is not None
and self.comp_info[comp_field_name] != self.aparams[param_name]
and not is_vm_stopped_or_will_be_stopped
):
check_errors = True
self.message(
f'Check for parameter "{param_name}" failed: '
f'VM must be stopped to change {param_name}.'
)
if self.aparams['preferred_cpu_cores'] is not None:
if not is_vm_stopped_or_will_be_stopped:
check_errors = True
self.message(
'Check for parameter "preferred_cpu_cores" failed: '
'VM must be stopped to change preferred_cpu_cores.'
)
if (
self.aparam_networks_has_dpdk
and not self.comp_info['hpBacked']
and not self.aparams['hp_backed']
):
check_errors = True
self.message(
'Check for parameter "networks" failed: '
'hp_backed must be set to True to connect a compute '
'to a DPDK network.'
)
if check_errors:
self.exit(fail=True)
# Workflow digest:
# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECSController
@@ -892,8 +1018,6 @@ def main():
elif amodule.params['state'] == 'paused':
subj.error()
else:
subj.check_amodule_args_for_create()
state = amodule.params['state']
if state is None:
state = 'present'