8.0.0
This commit is contained in:
@@ -111,6 +111,9 @@ class DecortAccount(DecortController):
|
||||
),
|
||||
),
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
required_one_of=[
|
||||
('id', 'name')
|
||||
@@ -358,6 +361,14 @@ class DecortAccount(DecortController):
|
||||
)
|
||||
if set(self.facts['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
|
||||
):
|
||||
result_args['description'] = aparam_desc
|
||||
|
||||
return result_args
|
||||
|
||||
|
||||
|
||||
@@ -173,6 +173,8 @@ class decort_disk(DecortController):
|
||||
ret_dict['computes'] = self.disk_info['computes']
|
||||
ret_dict['gid'] = self.disk_info['gid']
|
||||
ret_dict['iotune'] = self.disk_info['iotune']
|
||||
ret_dict['size_available'] = self.disk_info['sizeAvailable']
|
||||
ret_dict['size_used'] = self.disk_info['sizeUsed']
|
||||
|
||||
return ret_dict
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ class decort_group(DecortController):
|
||||
|
||||
if self.group_id:
|
||||
self.group_should_exist = True
|
||||
self.check_amodule_args_for_change()
|
||||
|
||||
return
|
||||
def nop(self):
|
||||
@@ -74,22 +75,34 @@ class decort_group(DecortController):
|
||||
return
|
||||
|
||||
def create(self):
|
||||
chipset = self.aparams['chipset']
|
||||
if chipset is None:
|
||||
chipset = 'i440fx'
|
||||
self.message(
|
||||
msg=f'Chipset not specified, '
|
||||
f'default value "{chipset}" will be used.',
|
||||
warning=True,
|
||||
)
|
||||
|
||||
self.group_id=self.group_provision(
|
||||
self.bservice_id,
|
||||
self.amodule.params['name'],
|
||||
self.amodule.params['count'],
|
||||
self.amodule.params['cpu'],
|
||||
self.amodule.params['ram'],
|
||||
self.amodule.params['boot_disk'],
|
||||
self.amodule.params['image_id'],
|
||||
self.amodule.params['driver'],
|
||||
self.amodule.params['role'],
|
||||
self.amodule.params['networks'],
|
||||
self.amodule.params['timeoutStart'],
|
||||
bs_id=self.bservice_id,
|
||||
arg_name=self.amodule.params['name'],
|
||||
arg_count=self.amodule.params['count'],
|
||||
arg_cpu=self.amodule.params['cpu'],
|
||||
arg_ram=self.amodule.params['ram'],
|
||||
arg_boot_disk=self.amodule.params['boot_disk'],
|
||||
arg_image_id=self.amodule.params['image_id'],
|
||||
arg_driver=self.amodule.params['driver'],
|
||||
arg_role=self.amodule.params['role'],
|
||||
arg_network=self.amodule.params['networks'],
|
||||
arg_timeout=self.amodule.params['timeoutStart'],
|
||||
chipset=chipset,
|
||||
)
|
||||
|
||||
if self.amodule.params['state'] in ('started','present'):
|
||||
self.group_state(self.bservice_id,self.group_id,self.amodule.params['state'])
|
||||
|
||||
self.group_should_exist = True
|
||||
return
|
||||
|
||||
def action(self):
|
||||
@@ -100,13 +113,26 @@ class decort_group(DecortController):
|
||||
):
|
||||
self.group_state(self.bservice_id,self.group_id,self.amodule.params['state'])
|
||||
|
||||
if self.aparams['count'] != None:
|
||||
aparam_chipset = self.aparams['chipset']
|
||||
if (
|
||||
self.aparams['count'] is not None
|
||||
and self.aparams['count'] != len(self.group_info['computes'])
|
||||
):
|
||||
self.group_resize_count(
|
||||
bs_id=self.bservice_id,
|
||||
gr_dict=self.group_info,
|
||||
desired_count=self.aparams['count'],
|
||||
chipset=aparam_chipset,
|
||||
)
|
||||
|
||||
if aparam_chipset is not None:
|
||||
for vm in self.group_info['computes']:
|
||||
if vm['chipset'] != aparam_chipset:
|
||||
self.compute_update(
|
||||
compute_id=vm['id'],
|
||||
chipset=aparam_chipset,
|
||||
)
|
||||
|
||||
for aparam_name, info_key in {'cpu': 'cpu',
|
||||
'boot_disk': 'disk',
|
||||
'role': 'role',
|
||||
@@ -254,6 +280,13 @@ class decort_group(DecortController):
|
||||
)
|
||||
)
|
||||
),
|
||||
chipset=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'Q35',
|
||||
'i440fx',
|
||||
]
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
@@ -268,6 +301,43 @@ class decort_group(DecortController):
|
||||
],
|
||||
)
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
check_errors = False
|
||||
|
||||
if (
|
||||
self.aparams['chipset'] is None
|
||||
and self.aparams['count'] > len(self.group_info['computes'])
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
msg='Check for parameter "chipset" failed: '
|
||||
'Chipset must be specified when increasing '
|
||||
'VM count in group'
|
||||
)
|
||||
|
||||
if (
|
||||
self.aparams['count'] is None
|
||||
or self.aparams['count'] == len(self.group_info['computes'])
|
||||
):
|
||||
aparam_chipset = self.aparams['chipset']
|
||||
if aparam_chipset is not None:
|
||||
for vm in self.group_info['computes']:
|
||||
if (
|
||||
vm['chipset'] != aparam_chipset
|
||||
and self.group_info['techStatus'] != 'STOPPED'
|
||||
and self.amodule.params['state'] != 'stopped'
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
msg=f'Check for parameter "chipset" failed: '
|
||||
f'group ID {self.group_id} must be stopped '
|
||||
f'to change chipset',
|
||||
)
|
||||
break
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
|
||||
def main():
|
||||
subj = decort_group()
|
||||
amodule = subj.amodule
|
||||
|
||||
@@ -121,6 +121,18 @@ class decort_k8s(DecortController):
|
||||
)
|
||||
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)
|
||||
|
||||
return
|
||||
|
||||
def package_facts(self,check_mode=False):
|
||||
@@ -155,6 +167,7 @@ class decort_k8s(DecortController):
|
||||
ret_dict['k8s_Masters'] = self.k8s_info['k8sGroups']['masters']
|
||||
ret_dict['k8s_Workers'] = self.k8s_info['k8sGroups']['workers']
|
||||
ret_dict['lb_id'] = self.k8s_info['lbId']
|
||||
ret_dict['description'] = self.k8s_info['desc']
|
||||
|
||||
return ret_dict
|
||||
|
||||
@@ -358,6 +371,7 @@ class decort_k8s(DecortController):
|
||||
master_count=dict(
|
||||
type='int',
|
||||
default=1,
|
||||
choices=[1, 3, 5, 7],
|
||||
),
|
||||
master_cpu=dict(
|
||||
type='int',
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
import re
|
||||
from typing import Sequence, Any, TypeVar
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
@@ -12,6 +14,9 @@ from ansible.module_utils.basic import env_fallback
|
||||
from ansible.module_utils.decort_utils import *
|
||||
|
||||
|
||||
DefaultT = TypeVar('DefaultT')
|
||||
|
||||
|
||||
class decort_kvmvm(DecortController):
|
||||
def __init__(self):
|
||||
# call superclass constructor first
|
||||
@@ -73,6 +78,8 @@ class decort_kvmvm(DecortController):
|
||||
|
||||
if not clone_id:
|
||||
clone_id = self.clone()
|
||||
if self.amodule.check_mode:
|
||||
self.exit()
|
||||
|
||||
self.comp_id, self.comp_info, self.rg_id = self._compute_get_by_id(
|
||||
comp_id=clone_id,
|
||||
@@ -178,8 +185,8 @@ class decort_kvmvm(DecortController):
|
||||
'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:
|
||||
# MTU for non-DPDK networks
|
||||
if (
|
||||
net['type'] != self.VMNetType.DPDK.value
|
||||
and net['mtu'] is not None
|
||||
@@ -191,6 +198,28 @@ class decort_kvmvm(DecortController):
|
||||
' (remove 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:
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks.mac" failed: '
|
||||
'MAC-address cannot be specified for an '
|
||||
'EMPTY type network.'
|
||||
)
|
||||
|
||||
mac_validation_result = re.match(
|
||||
'[0-9a-f]{2}([:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$',
|
||||
net['mac'].lower(),
|
||||
)
|
||||
if not mac_validation_result:
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks.mac" failed: '
|
||||
f'MAC-address for network ID {net["id"]} must be '
|
||||
'specified in quotes and in the format '
|
||||
'"XX:XX:XX:XX:XX:XX".'
|
||||
)
|
||||
|
||||
aparam_custom_fields = self.aparams['custom_fields']
|
||||
if aparam_custom_fields is not None:
|
||||
@@ -215,6 +244,22 @@ class decort_kvmvm(DecortController):
|
||||
'the list must contain only unique elements.'
|
||||
)
|
||||
|
||||
aparam_state = self.aparams['state']
|
||||
new_state = None
|
||||
match aparam_state:
|
||||
case 'halted' | 'poweredoff':
|
||||
new_state = 'stopped'
|
||||
case 'poweredon':
|
||||
new_state = 'started'
|
||||
|
||||
if new_state:
|
||||
self.message(
|
||||
msg=f'"{aparam_state}" state is deprecated and might be '
|
||||
f'removed in newer versions. '
|
||||
f'Please use "{new_state}" instead.',
|
||||
warning=True,
|
||||
)
|
||||
|
||||
if check_error:
|
||||
self.exit(fail=True)
|
||||
|
||||
@@ -265,12 +310,40 @@ class decort_kvmvm(DecortController):
|
||||
# each of the following calls will abort if argument is missing
|
||||
self.check_amodule_argument('cpu')
|
||||
self.check_amodule_argument('ram')
|
||||
|
||||
aparam_boot = self.aparams['boot']
|
||||
validated_bdisk_size = 0
|
||||
if self.amodule.params['boot'] is not None:
|
||||
boot_mode = 'bios'
|
||||
loader_type = 'unknown'
|
||||
if aparam_boot is not None:
|
||||
validated_bdisk_size = self.amodule.params['boot'].get(
|
||||
'disk_size', 0
|
||||
)
|
||||
|
||||
if aparam_boot['mode'] is None:
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='boot.mode',
|
||||
default_value=boot_mode
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
else:
|
||||
boot_mode = aparam_boot['mode']
|
||||
|
||||
if aparam_boot['loader_type'] is None:
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='boot.loader_type',
|
||||
default_value=loader_type
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
|
||||
else:
|
||||
loader_type = aparam_boot['loader_type']
|
||||
|
||||
|
||||
image_id, image_facts = None, None
|
||||
if self.aparam_image:
|
||||
# either image_name or image_id must be present
|
||||
@@ -308,7 +381,7 @@ class decort_kvmvm(DecortController):
|
||||
# Once this "feature" is fixed, make sure VM is created according to the actual desired state
|
||||
#
|
||||
start_compute = False # change this once a workaround for the aforementioned libvirt "feature" is implemented
|
||||
if self.amodule.params['state'] in ('halted', 'poweredoff'):
|
||||
if self.amodule.params['state'] in ('halted', 'poweredoff', 'stopped'):
|
||||
start_compute = False
|
||||
|
||||
if self.amodule.params['ssh_key'] and self.amodule.params['ssh_key_user'] and not self.amodule.params['ci_user_data']:
|
||||
@@ -334,7 +407,6 @@ class decort_kvmvm(DecortController):
|
||||
if numa_affinity is None:
|
||||
numa_affinity = 'none'
|
||||
|
||||
|
||||
chipset = self.amodule.params['chipset']
|
||||
if chipset is None:
|
||||
chipset = 'i440fx'
|
||||
@@ -343,6 +415,28 @@ class decort_kvmvm(DecortController):
|
||||
f'default value "{chipset}" will be used.',
|
||||
warning=True,
|
||||
)
|
||||
|
||||
network_interface_naming = self.aparams['network_interface_naming']
|
||||
if network_interface_naming is None:
|
||||
network_interface_naming = 'ens'
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='network_interface_naming',
|
||||
default_value=network_interface_naming
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
|
||||
hot_resize = self.aparams['hot_resize']
|
||||
if hot_resize is None:
|
||||
hot_resize = False
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='hot_resize',
|
||||
default_value=hot_resize
|
||||
),
|
||||
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
|
||||
@@ -360,7 +454,11 @@ class decort_kvmvm(DecortController):
|
||||
cpu_pin=cpu_pin,
|
||||
hp_backed=hp_backed,
|
||||
numa_affinity=numa_affinity,
|
||||
preferred_cpu_cores=self.amodule.params['preferred_cpu_cores'],)
|
||||
preferred_cpu_cores=self.amodule.params['preferred_cpu_cores'],
|
||||
boot_mode=boot_mode,
|
||||
boot_loader_type=loader_type,
|
||||
network_interface_naming=network_interface_naming,
|
||||
hot_resize=hot_resize,)
|
||||
self.comp_should_exist = True
|
||||
|
||||
# Originally we would have had to re-read comp_info after VM was provisioned
|
||||
@@ -407,7 +505,7 @@ class decort_kvmvm(DecortController):
|
||||
label=self.amodule.params['affinity_label'],)
|
||||
# NOTE: see NOTE above regarding libvirt "feature" and new VMs created in HALTED state
|
||||
if self.aparam_image:
|
||||
if self.amodule.params['state'] not in ('halted', 'poweredoff'):
|
||||
if self.amodule.params['state'] in ('poweredon', 'started'):
|
||||
self.compute_powerstate(self.comp_info, 'started')
|
||||
|
||||
if self.aparams['custom_fields'] is None:
|
||||
@@ -553,14 +651,54 @@ class decort_kvmvm(DecortController):
|
||||
'description': 'desc',
|
||||
'auto_start': 'autoStart',
|
||||
'preferred_cpu_cores': 'preferredCpu',
|
||||
'boot.mode': 'bootType',
|
||||
'boot.loader_type': 'loaderType',
|
||||
'network_interface_naming': 'networkInterfaceNaming',
|
||||
'hot_resize': 'hotResize',
|
||||
}
|
||||
|
||||
def get_nested_value(
|
||||
d: dict,
|
||||
keys: Sequence[str],
|
||||
default: DefaultT | None = None,
|
||||
) -> Any | DefaultT:
|
||||
if not keys:
|
||||
raise ValueError
|
||||
|
||||
key = keys[0]
|
||||
if key not in d:
|
||||
return default
|
||||
value = d[key]
|
||||
|
||||
if len(keys) > 1:
|
||||
if isinstance(value, dict):
|
||||
nested_d = value
|
||||
return get_nested_value(
|
||||
d=nested_d,
|
||||
keys=keys[1:],
|
||||
default=default,
|
||||
)
|
||||
if value is None:
|
||||
return default
|
||||
raise ValueError(
|
||||
f'The key {key} found, but its value is not a dictionary.'
|
||||
)
|
||||
return value
|
||||
|
||||
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[aparam_name] = aparam_value
|
||||
aparam_value = get_nested_value(
|
||||
d=self.aparams,
|
||||
keys=aparam_name.split('.'),
|
||||
)
|
||||
comp_value = get_nested_value(
|
||||
d=self.comp_info,
|
||||
keys=comp_field_name.split('.'),
|
||||
)
|
||||
|
||||
if aparam_value is not None and aparam_value != comp_value:
|
||||
result_args[aparam_name.replace('.', '_')] = (
|
||||
aparam_value
|
||||
)
|
||||
|
||||
return result_args
|
||||
|
||||
@@ -679,15 +817,49 @@ class decort_kvmvm(DecortController):
|
||||
ret_dict['clones'] = self.comp_info['clones']
|
||||
ret_dict['clone_reference'] = self.comp_info['cloneReference']
|
||||
|
||||
ret_dict['boot_mode'] = self.comp_info['bootType']
|
||||
ret_dict['boot_loader_type'] = self.comp_info['loaderType']
|
||||
ret_dict['network_interface_naming'] = self.comp_info[
|
||||
'networkInterfaceNaming'
|
||||
]
|
||||
ret_dict['hot_resize'] = self.comp_info['hotResize']
|
||||
|
||||
ret_dict['pinned_to_stack'] = self.comp_info['pinnedToStack']
|
||||
|
||||
ret_dict['affinity_label'] = self.comp_info['affinityLabel']
|
||||
ret_dict['affinity_rules'] = self.comp_info['affinityRules']
|
||||
ret_dict['anti_affinity_rules'] = self.comp_info['antiAffinityRules']
|
||||
|
||||
return ret_dict
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
check_errors = False
|
||||
# Check for unacceptable parameters for a blank Compute
|
||||
if (
|
||||
self.aparams['image_id'] is not None
|
||||
or self.aparams['image_name'] is not None
|
||||
):
|
||||
self.aparam_image = True
|
||||
for param in (
|
||||
'network_interface_naming',
|
||||
'hot_resize',
|
||||
):
|
||||
if self.aparams[param] is not None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "{param}" failed: '
|
||||
'parameter can be specified only for a blank VM.'
|
||||
)
|
||||
|
||||
if self.aparams['boot'] is not None:
|
||||
for param in ('mode', 'loader_type'):
|
||||
if self.aparams['boot'][param] is not None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "boot.{param}" failed: '
|
||||
'parameter can be specified only for a blank VM.'
|
||||
)
|
||||
|
||||
else:
|
||||
self.aparam_image = False
|
||||
if (
|
||||
@@ -696,14 +868,15 @@ class decort_kvmvm(DecortController):
|
||||
'present',
|
||||
'poweredoff',
|
||||
'halted',
|
||||
'stopped',
|
||||
)
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "state" failed: '
|
||||
'state for a blank Compute must be either '
|
||||
'"present", "poweredoff" or "halted".'
|
||||
'"present" or "stopped".'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
for parameter in (
|
||||
'ssh_key',
|
||||
@@ -711,38 +884,41 @@ class decort_kvmvm(DecortController):
|
||||
'ci_user_data',
|
||||
):
|
||||
if self.aparams[parameter] is not None:
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "{parameter}" failed: '
|
||||
f'"image_id" or "image_name" must be specified '
|
||||
f'to set {parameter}.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
if (
|
||||
self.aparams['sep_id'] is not None
|
||||
and self.aparams['boot'] is None
|
||||
and self.aparams['boot']['disk_size'] is None
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "sep_id" failed: '
|
||||
'"image_id" or "image_name" or "boot.disk_size" '
|
||||
'must be specified to set sep_id.'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
if self.aparams['rollback_to'] is not None:
|
||||
check_errors = True
|
||||
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']:
|
||||
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)
|
||||
|
||||
@property
|
||||
@@ -769,6 +945,21 @@ class decort_kvmvm(DecortController):
|
||||
disk_size=dict(
|
||||
type='int',
|
||||
),
|
||||
mode=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'bios',
|
||||
'uefi',
|
||||
],
|
||||
),
|
||||
loader_type=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'windows',
|
||||
'linux',
|
||||
'unknown',
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
sep_id=dict(
|
||||
@@ -840,6 +1031,9 @@ class decort_kvmvm(DecortController):
|
||||
mtu=dict(
|
||||
type='int',
|
||||
),
|
||||
mac=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
required_if=[
|
||||
('type', 'VINS', ('id',)),
|
||||
@@ -892,6 +1086,8 @@ class decort_kvmvm(DecortController):
|
||||
'poweredoff',
|
||||
'halted',
|
||||
'poweredon',
|
||||
'stopped',
|
||||
'started',
|
||||
'present',
|
||||
],
|
||||
),
|
||||
@@ -974,6 +1170,16 @@ class decort_kvmvm(DecortController):
|
||||
),
|
||||
),
|
||||
),
|
||||
network_interface_naming=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'ens',
|
||||
'eth',
|
||||
],
|
||||
),
|
||||
hot_resize=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
@@ -1069,12 +1275,14 @@ class decort_kvmvm(DecortController):
|
||||
|
||||
if (
|
||||
not comp_info['imageId']
|
||||
and self.amodule.params['state'] in ('poweredon', 'paused')
|
||||
and self.amodule.params['state'] in (
|
||||
'poweredon', 'paused', 'started',
|
||||
)
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameter "state" failed: '
|
||||
'state for a blank Compute can not be "poweredon" or "paused".'
|
||||
'state for a blank Compute can not be "started" or "paused".'
|
||||
)
|
||||
|
||||
is_vm_stopped_or_will_be_stopped = (
|
||||
@@ -1083,14 +1291,14 @@ class decort_kvmvm(DecortController):
|
||||
and (
|
||||
self.amodule.params['state'] is None
|
||||
or self.amodule.params['state'] in (
|
||||
'halted', 'poweredoff', 'present',
|
||||
'halted', 'poweredoff', 'present', 'stopped',
|
||||
)
|
||||
)
|
||||
)
|
||||
or (
|
||||
comp_info['techStatus'] != 'STOPPED'
|
||||
and self.amodule.params['state'] in (
|
||||
'halted', 'poweredoff',
|
||||
'halted', 'poweredoff', 'stopped',
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -1120,6 +1328,7 @@ class decort_kvmvm(DecortController):
|
||||
'cpu_pin': 'cpupin',
|
||||
'hp_backed': 'hpBacked',
|
||||
'numa_affinity': 'numaAffinity',
|
||||
'hot_resize': 'hotResize',
|
||||
}
|
||||
for param_name, comp_field_name in params_to_check.items():
|
||||
if (
|
||||
@@ -1159,13 +1368,13 @@ class decort_kvmvm(DecortController):
|
||||
and (
|
||||
self.amodule.params['state'] is None
|
||||
or self.amodule.params['state'] in (
|
||||
'poweredon', 'present',
|
||||
'poweredon', 'present', 'started',
|
||||
)
|
||||
)
|
||||
)
|
||||
or (
|
||||
comp_info['techStatus'] != 'STARTED'
|
||||
and self.amodule.params['state'] == 'poweredon'
|
||||
and self.amodule.params['state'] in ('poweredon', 'started')
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1179,30 +1388,55 @@ class decort_kvmvm(DecortController):
|
||||
|
||||
aparam_disks = self.aparams['disks']
|
||||
if aparam_disks is not None:
|
||||
if self.comp_info['snapSets']:
|
||||
match aparam_disks['mode']:
|
||||
case 'detach' | 'delete':
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "disks" failed: '
|
||||
f'cannot {aparam_disks["mode"]} disks for '
|
||||
f'Compute ID {self.comp_id} while snapshots '
|
||||
f'exist in compute.'
|
||||
)
|
||||
case 'match':
|
||||
comp_disk_ids = {
|
||||
disk['id'] for disk in self.comp_info['disks']
|
||||
}
|
||||
disks = set(aparam_disks['ids'])
|
||||
disks_to_detach = comp_disk_ids - disks
|
||||
if disks_to_detach:
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "disks" failed: '
|
||||
f'disks {disks_to_detach} cannot be detached '
|
||||
f'from Compute ID {self.comp_id} while '
|
||||
f'snapshots exist in compute.'
|
||||
)
|
||||
aparam_disks_ids = aparam_disks['ids']
|
||||
comp_boot_disk_id = None
|
||||
for comp_disk in self.comp_info['disks']:
|
||||
if comp_disk['type'] == 'B':
|
||||
comp_boot_disk_id = comp_disk['id']
|
||||
break
|
||||
disks_to_detach = []
|
||||
match aparam_disks['mode']:
|
||||
case 'detach' | 'delete':
|
||||
disks_to_detach = aparam_disks_ids
|
||||
case 'match':
|
||||
comp_disk_ids = {
|
||||
disk['id'] for disk in self.comp_info['disks']
|
||||
}
|
||||
disks = set(aparam_disks_ids)
|
||||
disks_to_detach = comp_disk_ids - disks
|
||||
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
|
||||
):
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "disks" failed: '
|
||||
f'VM ID {comp_id} must be stopped to detach '
|
||||
f'boot disk ID {comp_boot_disk_id}.'
|
||||
)
|
||||
if self.comp_info['snapSets'] and disks_to_detach:
|
||||
check_errors = True
|
||||
self.message(
|
||||
f'Check for parameter "disks" failed: '
|
||||
f'cannot detach disks {disks_to_detach} from '
|
||||
f'Compute ID {self.comp_id} while snapshots exist.'
|
||||
)
|
||||
|
||||
if (
|
||||
(
|
||||
self.aparams['cpu'] is not None
|
||||
and self.aparams['cpu'] != comp_info['cpus']
|
||||
) or (
|
||||
self.aparams['ram'] is not None
|
||||
and self.aparams['ram'] != comp_info['ram']
|
||||
)
|
||||
) and not (self.aparams['hot_resize'] or comp_info['hotResize']):
|
||||
check_errors = True
|
||||
self.message(
|
||||
'Check for parameters "cpu" and "ram" failed: '
|
||||
'Hot resize must be enabled to change CPU or RAM.'
|
||||
)
|
||||
|
||||
if check_errors:
|
||||
self.exit(fail=True)
|
||||
@@ -1323,7 +1557,8 @@ def main():
|
||||
subj.destroy()
|
||||
else:
|
||||
if amodule.params['state'] in (
|
||||
'paused', 'poweredon', 'poweredoff', 'halted'
|
||||
'paused', 'poweredon', 'poweredoff',
|
||||
'halted', 'started', 'stopped',
|
||||
):
|
||||
subj.compute_powerstate(
|
||||
comp_facts=subj.comp_info,
|
||||
@@ -1331,7 +1566,7 @@ def main():
|
||||
)
|
||||
subj.modify(arg_wait_cycles=7)
|
||||
elif subj.comp_info['status'] == "DELETED":
|
||||
if amodule.params['state'] in ('present', 'poweredon'):
|
||||
if amodule.params['state'] in ('present', 'poweredon', 'started'):
|
||||
# TODO - check if restore API returns VM ID (similarly to VM create API)
|
||||
subj.compute_restore(comp_id=subj.comp_id)
|
||||
# TODO - do we need updated comp_info to manage port forwards and size after VM is restored?
|
||||
@@ -1345,10 +1580,15 @@ def main():
|
||||
# subj.nop()
|
||||
# subj.comp_should_exist = False
|
||||
subj.destroy()
|
||||
elif amodule.params['state'] in ('paused', 'poweredoff', 'halted'):
|
||||
elif amodule.params['state'] in (
|
||||
'paused', 'poweredoff', 'halted', 'stopped'
|
||||
):
|
||||
subj.error()
|
||||
elif subj.comp_info['status'] == "DESTROYED":
|
||||
if amodule.params['state'] in ('present', 'poweredon', 'poweredoff', 'halted'):
|
||||
if amodule.params['state'] in (
|
||||
'present', 'poweredon', 'poweredoff',
|
||||
'halted', 'started', 'stopped',
|
||||
):
|
||||
subj.create() # this call will also handle data disk & network connection
|
||||
elif amodule.params['state'] == 'absent':
|
||||
subj.nop()
|
||||
@@ -1363,7 +1603,10 @@ def main():
|
||||
# If requested state is 'absent' - nothing to do
|
||||
if state == 'absent':
|
||||
subj.nop()
|
||||
elif state in ('present', 'poweredon', 'poweredoff', 'halted'):
|
||||
elif state in (
|
||||
'present', 'poweredon', 'poweredoff',
|
||||
'halted', 'started', 'stopped',
|
||||
):
|
||||
subj.create() # this call will also handle data disk & network connection
|
||||
elif state == 'paused':
|
||||
subj.error()
|
||||
|
||||
@@ -72,13 +72,61 @@ class decort_osimage(DecortController):
|
||||
|
||||
|
||||
def decort_image_create(self,amodule):
|
||||
aparam_boot = self.aparams['boot']
|
||||
boot_mode = 'bios'
|
||||
loader_type = 'unknown'
|
||||
if aparam_boot is not None:
|
||||
if aparam_boot['mode'] is None:
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='boot.mode',
|
||||
default_value=boot_mode
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
else:
|
||||
boot_mode = aparam_boot['mode']
|
||||
|
||||
if aparam_boot['loader_type'] is None:
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='boot.loader_type',
|
||||
default_value=loader_type
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
else:
|
||||
loader_type = aparam_boot['loader_type']
|
||||
|
||||
network_interface_naming = self.aparams['network_interface_naming']
|
||||
if network_interface_naming is None:
|
||||
network_interface_naming = 'ens'
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='network_interface_naming',
|
||||
default_value=network_interface_naming
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
|
||||
hot_resize = self.aparams['hot_resize']
|
||||
if hot_resize is None:
|
||||
hot_resize = False
|
||||
self.message(
|
||||
msg=self.MESSAGES.default_value_used(
|
||||
param_name='hot_resize',
|
||||
default_value=hot_resize
|
||||
),
|
||||
warning=True,
|
||||
)
|
||||
|
||||
# function that creates OS image
|
||||
image_facts = self.image_create(img_name=self.validated_image_name,
|
||||
url=amodule.params['url'],
|
||||
gid=amodule.params['gid'],
|
||||
boottype=amodule.params['boottype'],
|
||||
imagetype=amodule.params['imagetype'],
|
||||
hotresize=amodule.params['hotresize'],
|
||||
boot_mode=boot_mode,
|
||||
boot_loader_type=loader_type,
|
||||
hot_resize=hot_resize,
|
||||
username=amodule.params['image_username'],
|
||||
password=amodule.params['image_password'],
|
||||
account_Id=self.validated_account_id,
|
||||
@@ -86,7 +134,8 @@ class decort_osimage(DecortController):
|
||||
passwordDL=amodule.params['passwordDL'],
|
||||
sepId=amodule.params['sepId'],
|
||||
poolName=amodule.params['poolName'],
|
||||
drivers=amodule.params['drivers'])
|
||||
drivers=amodule.params['drivers'],
|
||||
network_interface_naming=network_interface_naming)
|
||||
self.result['changed'] = True
|
||||
return image_facts
|
||||
|
||||
@@ -126,7 +175,11 @@ class decort_osimage(DecortController):
|
||||
image_id, image_facts = self.decort_virt_image_find(amodule)
|
||||
return image_id, image_facts
|
||||
|
||||
def decort_osimage_package_facts(arg_osimage_facts, arg_check_mode=False):
|
||||
@staticmethod
|
||||
def decort_osimage_package_facts(
|
||||
arg_osimage_facts: dict | None,
|
||||
arg_check_mode=False,
|
||||
):
|
||||
"""Package a dictionary of OS image according to the decort_osimage module specification. This
|
||||
dictionary will be returned to the upstream Ansible engine at the completion of the module run.
|
||||
|
||||
@@ -154,13 +207,26 @@ class decort_osimage(DecortController):
|
||||
ret_dict['id'] = arg_osimage_facts['id']
|
||||
ret_dict['name'] = arg_osimage_facts['name']
|
||||
ret_dict['size'] = arg_osimage_facts['size']
|
||||
ret_dict['type'] = arg_osimage_facts['type']
|
||||
# ret_dict['arch'] = arg_osimage_facts['architecture']
|
||||
ret_dict['sep_id'] = arg_osimage_facts['sepId']
|
||||
ret_dict['pool'] = arg_osimage_facts['pool']
|
||||
ret_dict['state'] = arg_osimage_facts['status']
|
||||
ret_dict['linkto'] = arg_osimage_facts['linkTo']
|
||||
ret_dict['accountId'] = arg_osimage_facts['accountId']
|
||||
ret_dict['boot_mode'] = arg_osimage_facts['bootType']
|
||||
|
||||
ret_dict['boot_loader_type'] = ''
|
||||
match arg_osimage_facts['type']:
|
||||
case 'cdrom' | 'virtual' as type:
|
||||
ret_dict['type'] = type
|
||||
case _ as boot_loader_type:
|
||||
ret_dict['type'] = 'template'
|
||||
ret_dict['boot_loader_type'] = boot_loader_type
|
||||
|
||||
ret_dict['network_interface_naming'] = arg_osimage_facts[
|
||||
'networkInterfaceNaming'
|
||||
]
|
||||
ret_dict['hot_resize'] = arg_osimage_facts['hotResize']
|
||||
return ret_dict
|
||||
|
||||
@property
|
||||
@@ -207,14 +273,6 @@ class decort_osimage(DecortController):
|
||||
type='str',
|
||||
default='KVM_X86',
|
||||
),
|
||||
imagetype=dict(
|
||||
type='str',
|
||||
default='linux',
|
||||
),
|
||||
boottype=dict(
|
||||
type='str',
|
||||
default='uefi',
|
||||
),
|
||||
url=dict(
|
||||
type='str',
|
||||
),
|
||||
@@ -229,9 +287,8 @@ class decort_osimage(DecortController):
|
||||
poolName=dict(
|
||||
type='str',
|
||||
),
|
||||
hotresize=dict(
|
||||
hot_resize=dict(
|
||||
type='bool',
|
||||
default=False,
|
||||
),
|
||||
image_username=dict(
|
||||
type='str',
|
||||
@@ -245,6 +302,33 @@ class decort_osimage(DecortController):
|
||||
passwordDL=dict(
|
||||
type='str',
|
||||
),
|
||||
boot=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
mode=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'bios',
|
||||
'uefi',
|
||||
],
|
||||
),
|
||||
loader_type=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'windows',
|
||||
'linux',
|
||||
'unknown',
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
network_interface_naming=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'ens',
|
||||
'eth',
|
||||
],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@@ -117,29 +117,29 @@ class DecortUserInfo(DecortController):
|
||||
start=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
unix=dict(
|
||||
timestamp=dict(
|
||||
type='int',
|
||||
),
|
||||
date_time=dict(
|
||||
datetime=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
('unix', 'date_time'),
|
||||
('timestamp', 'datetime'),
|
||||
],
|
||||
),
|
||||
end=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
unix=dict(
|
||||
timestamp=dict(
|
||||
type='int',
|
||||
),
|
||||
date_time=dict(
|
||||
datetime=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
('unix', 'date_time'),
|
||||
('timestamp', 'datetime'),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -160,6 +160,23 @@ class DecortUserInfo(DecortController):
|
||||
),
|
||||
),
|
||||
),
|
||||
sorting=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
asc=dict(
|
||||
type='bool',
|
||||
default=True,
|
||||
),
|
||||
field=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
e.value
|
||||
for e in self.AuditsSortableField
|
||||
],
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
objects_search=dict(
|
||||
@@ -184,7 +201,7 @@ class DecortUserInfo(DecortController):
|
||||
|
||||
match self.aparams['audits']:
|
||||
case {'filter': {'time':
|
||||
{'start': {'date_time': str() as dt_str}}
|
||||
{'start': {'datetime': str() as dt_str}}
|
||||
}
|
||||
}:
|
||||
if self.dt_str_to_sec(dt_str=dt_str) is None:
|
||||
@@ -192,7 +209,7 @@ class DecortUserInfo(DecortController):
|
||||
check_error = True
|
||||
match self.aparams['audits']:
|
||||
case {'filter': {'time':
|
||||
{'end': {'date_time': str() as dt_str}}
|
||||
{'end': {'datetime': str() as dt_str}}
|
||||
}
|
||||
}:
|
||||
if self.dt_str_to_sec(dt_str=dt_str) is None:
|
||||
@@ -283,16 +300,16 @@ class DecortUserInfo(DecortController):
|
||||
mapped_args['max_status_code'] = max_status_code
|
||||
|
||||
match input_args_filter['time']:
|
||||
case {'start': {'unix': int() as start_unix_time}}:
|
||||
case {'start': {'timestamp': int() as start_unix_time}}:
|
||||
mapped_args['start_unix_time'] = start_unix_time
|
||||
case {'start': {'date_time': str() as start_dt_str}}:
|
||||
case {'start': {'datetime': str() as start_dt_str}}:
|
||||
mapped_args['start_unix_time'] = self.dt_str_to_sec(
|
||||
dt_str=start_dt_str
|
||||
)
|
||||
match input_args_filter['time']:
|
||||
case {'end': {'unix': int() as end_unix_time}}:
|
||||
case {'end': {'timestamp': int() as end_unix_time}}:
|
||||
mapped_args['end_unix_time'] = end_unix_time
|
||||
case {'end': {'date_time': str() as end_dt_str}}:
|
||||
case {'end': {'datetime': str() as end_dt_str}}:
|
||||
mapped_args['end_unix_time'] = self.dt_str_to_sec(
|
||||
dt_str=end_dt_str
|
||||
)
|
||||
@@ -302,6 +319,16 @@ class DecortUserInfo(DecortController):
|
||||
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.AuditsSortableField(input_args_sorting_field)
|
||||
)
|
||||
|
||||
return mapped_args
|
||||
|
||||
def run(self):
|
||||
|
||||
Reference in New Issue
Block a user