This commit is contained in:
2024-11-13 11:51:38 +03:00
parent aa3f84095f
commit dd2fca15f3
32 changed files with 9538 additions and 713 deletions

View File

@@ -107,6 +107,21 @@ class DecortAccount(DecortController):
],
default='present',
),
sep_pools=dict(
type='list',
elements='dict',
options=dict(
sep_id=dict(
type='int',
required=True,
),
pool_names=dict(
type='list',
required=True,
elements='str',
),
),
),
),
required_one_of=[
('id', 'name')
@@ -344,6 +359,16 @@ class DecortAccount(DecortController):
and current_value != aparam_quotas[aparam]):
result_args[result_arg] = aparam_quotas[aparam]
aparam_sep_pools = self.aparams['sep_pools']
if aparam_sep_pools is not None:
sep_pools = set()
for sep in aparam_sep_pools:
for pool_name in sep['pool_names']:
sep_pools.add(
f'{sep["sep_id"]}_{pool_name}'
)
if set(self.facts['uniqPools']) != sep_pools:
result_args['sep_pools'] = sep_pools
return result_args

View File

@@ -195,10 +195,6 @@ class DecortAccountInfo(DecortController):
images=dict(
type='dict',
options=dict(
deleted=dict(
type='bool',
default=False,
),
filter=dict(
type='dict',
options=dict(
@@ -312,7 +308,7 @@ class DecortAccountInfo(DecortController):
type='dict',
options=dict(
ext_ip=dict(
type='int',
type='str',
),
id=dict(
type='int',
@@ -478,7 +474,6 @@ class DecortAccountInfo(DecortController):
return input_args
mapped_args = {}
mapped_args['deleted'] = input_args['deleted']
if input_args['filter']:
mapped_args['image_id'] = input_args['filter']['id']
mapped_args['image_name'] = input_args['filter']['name']

View File

@@ -23,7 +23,6 @@ class decort_bservice(DecortController):
validated_acc_id = 0
validated_rg_id = 0
validated_rg_facts = None
self.bservice_info = None
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] == 0:
self.result['failed'] = True
@@ -55,12 +54,12 @@ class decort_bservice(DecortController):
arg_amodule.params['rg_name'])
self.fail_json(**self.result)
arg_amodule.params['rg_id'] = validated_rg_id
arg_amodule.params['rg_name'] = validated_rg_facts['name']
self.acc_id = validated_rg_facts['accountId']
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(
self.acc_id,
self.bservice_id, self.bservice_info = self.bservice_find(
validated_acc_id,
validated_rg_id,
arg_amodule.params['name'],
arg_amodule.params['id']
@@ -150,7 +149,7 @@ class decort_bservice(DecortController):
ret_dict['techStatus'] = self.bservice_info['techStatus']
ret_dict['state'] = self.bservice_info['status']
ret_dict['rg_id'] = self.bservice_info['rgId']
ret_dict['account_id'] = self.acc_id
ret_dict['account_id'] = self.bservice_info['accountId']
ret_dict['groups'] = self.bservice_info['groups']
return ret_dict
@staticmethod
@@ -188,7 +187,7 @@ class decort_bservice(DecortController):
user=dict(type='str',
required=False,
fallback=(env_fallback, ['DECORT_USER'])),
name=dict(type='str', required=True),
name=dict(type='str', required=False, default=''),
sshuser=dict(type='str', required=False,default=None),
sshkey=dict(type='str', required=False,default=None),
id=dict(type='int', required=False, default=0),

View File

@@ -240,7 +240,7 @@ facts:
pool: datastore
state: ASSIGNED
account_id: 7
attached_to: 18
computes: {vm_id: vm_name}
gid: 1001
'''
@@ -257,39 +257,45 @@ class decort_disk(DecortController):
validated_acc_id = 0
validated_acc_info = None
validated_disk_id = 0
self.disk_id = 0
self.account_id = 0
validated_disk_facts = None
# limitIO check for exclusive parameters
if arg_amodule.params['limitIO']:
self.disk_check_iotune_arg(arg_amodule.params['limitIO'])
if arg_amodule.params['id'] or arg_amodule.params['name']:
if arg_amodule.params['account_id'] or arg_amodule.params['account_name'] :
validated_acc_id,validated_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['changed'] = False
self.result['msg'] = ("Current user does not have access to the account ID {} / "
"name '{}' or non-existent account specified.").format(arg_amodule.params['account_id'],
arg_amodule.params['account_name'])
self.amodule.fail_json(**self.result)
else:
self.acc_id = validated_acc_id
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 "",
account_id=self.acc_id,
check_state=False,
if not arg_amodule.params['id']:
if (
not arg_amodule.params['account_id']
and not arg_amodule.params['account_name']
):
self.result['changed'] = False
self.result['msg'] = (
'Cannot manage Disk by name without specifying account ID '
'or name.'
)
else:
self.result['failed'] = True
self.result['msg'] = ("Cannot manage Disk when its ID is 0 and name is empty")
self.amodule.fail_json(**self.result)
self.amodule.fail_json(**self.result)
validated_acc_id, validated_acc_info = self.account_find(
arg_amodule.params['account_name'],
arg_amodule.params['account_id'])
if not validated_acc_id:
self.result['changed'] = False
self.result['msg'] = (
f"Current user does not have access to the account "
f"ID {arg_amodule.params['account_id']} / "
f"name '{arg_amodule.params['account_name']}' "
f"or non-existent account specified."
)
self.amodule.fail_json(**self.result)
self.acc_id = validated_acc_id
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 "",
account_id=self.acc_id,
check_state=False,
)
if arg_amodule.params['place_with']:
image_id, image_facts = self.image_find(arg_amodule.params['place_with'], "", 0)
@@ -324,12 +330,17 @@ class decort_disk(DecortController):
if restore:
self.disk_restore(self.disk_id)
#rename if id present
if self.amodule.params['name'] != self.disk_info['name']:
if (
self.amodule.params['name'] is not None
and self.amodule.params['name'] != self.disk_info['name']
):
self.disk_rename(disk_id=self.disk_id,
name=self.amodule.params['name'])
self.disk_info['name'] = self.amodule.params['name']
#resize
if self.amodule.params['size'] != self.disk_info['sizeMax']:
if (
self.amodule.params['size'] is not None
and self.amodule.params['size'] != self.disk_info['sizeMax']
):
self.disk_resize(self.disk_info,self.amodule.params['size'])
#IO TUNE
if self.amodule.params['limitIO']:
@@ -381,7 +392,6 @@ class decort_disk(DecortController):
account_id=0,
sep_id=0,
pool="none",
attached_to=0,
gid=0
)
@@ -399,7 +409,7 @@ class decort_disk(DecortController):
ret_dict['account_id'] = self.disk_info['accountId']
ret_dict['sep_id'] = self.disk_info['sepId']
ret_dict['pool'] = self.disk_info['pool']
ret_dict['attached_to'] = self.disk_info['vmid']
ret_dict['computes'] = self.disk_info['computes']
ret_dict['gid'] = self.disk_info['gid']
ret_dict['iotune'] = self.disk_info['iotune']
@@ -441,7 +451,7 @@ class decort_disk(DecortController):
place_with=dict(type='int', default=0),
pool=dict(type='str', default=''),
sep_id=dict(type='int', default=0),
size=dict(type='int', default=0),
size=dict(type='int'),
type=dict(type='str',
required=False,
default="D",
@@ -490,6 +500,9 @@ def main():
['app_id', 'app_secret'],
['user', 'password'],
],
required_one_of=[
['id', 'name'],
],
)
decon = decort_disk(amodule)

View File

@@ -77,13 +77,6 @@ class decort_group(DecortController):
return
def create(self):
if self.amodule.params['driver'] not in ["KVM_X86","KVM_PPC"]:
self.result['failed'] = True
self.result['msg'] = ("Unsupported driver '{}' is specified for "
"Group.").format(self.amodule.params['driver'])
self.amodule.fail_json(**self.result)
self.group_id=self.group_provision(
self.bservice_id,
self.amodule.params['name'],
@@ -109,22 +102,40 @@ class decort_group(DecortController):
self.group_info['techStatus'] == 'STOPPED' and self.amodule.params['state'] in ('started','present')
):
self.group_state(self.bservice_id,self.group_id,self.amodule.params['state'])
self.group_resize_count(self.bservice_id,self.group_info,self.amodule.params['count'])
self.group_update_hw(
self.bservice_id,
self.group_info,
self.amodule.params['cpu'],
self.amodule.params['boot_disk'],
self.amodule.params['name'],
self.amodule.params['role'],
self.amodule.params['ram'],
)
self.group_update_net(
self.bservice_id,
self.group_info,
self.amodule.params['networks']
)
return
if self.aparams['count'] != None:
self.group_resize_count(
bs_id=self.bservice_id,
gr_dict=self.group_info,
desired_count=self.aparams['count'],
)
for aparam_name, info_key in {'cpu': 'cpu',
'boot_disk': 'disk',
'role': 'role',
'ram': 'ram',
'name': 'name',
}.items():
aparam_value = self.aparams[aparam_name]
group_info_value = self.group_info[info_key]
if aparam_value != None and aparam_value != group_info_value:
self.group_update(
bs_id=self.bservice_id,
gr_dict=self.group_info,
arg_cpu=self.aparams['cpu'],
arg_disk=self.aparams['boot_disk'],
arg_name=self.aparams['name'],
arg_role=self.aparams['role'],
arg_ram=self.aparams['ram'],
)
break
if self.aparams['networks'] != None:
self.group_update_net(
bs_id=self.bservice_id,
gr_dict=self.group_info,
arg_net=self.aparams['networks'],
)
def destroy(self):
@@ -198,20 +209,20 @@ class decort_group(DecortController):
user=dict(type='str',
required=False,
fallback=(env_fallback, ['DECORT_USER'])),
name=dict(type='str', required=True),
id=dict(type='int', required=False, default=0),
name=dict(type='str'),
id=dict(type='int', required=False),
image_id=dict(type='int', required=False),
image_name=dict(type='str', required=False),
driver=dict(type='str', required=False,default="KVM_X86"),
driver=dict(type='str', required=False, choices=['KVM_X86', 'SVA_KVM_X86'], default="KVM_X86"),
boot_disk=dict(type='int', required=False),
bservice_id=dict(type='int', required=True),
count=dict(type='int', required=True),
count=dict(type='int'),
timeoutStart=dict(type='int', required=False),
role=dict(type='str', required=False, default=''),
role=dict(type='str', required=False),
cpu=dict(type='int', required=False),
ram=dict(type='int', required=False),
networks=dict(
type='list', default=[], elements='dict',
type='list', elements='dict',
options=dict(
type=dict(
type='str',
@@ -244,6 +255,13 @@ def main():
],
required_one_of=[
['id', 'name'],
['id', 'networks'],
['id', 'count'],
['id', 'cpu'],
['id', 'ram'],
['id', 'boot_disk'],
['id', 'image_id'],
['id', 'driver'],
],
)

View File

@@ -63,6 +63,7 @@ EXAMPLES = '''
- node.mainapp.domen.local/group1=g1
register: some_cluster
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
@@ -76,20 +77,14 @@ class decort_k8s(DecortController):
validated_rg_facts = None
validated_k8ci_id = 0
self.k8s_should_exist = False
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['name'] == "" and arg_amodule.params['id'] == 0:
if arg_amodule.params['name'] == "" and arg_amodule.params['id'] is None:
self.result['failed'] = True
self.result['changed'] = False
self.result['msg'] = "Cannot manage k8s cluster when its ID is 0 and name is empty."
self.amodule.fail_json(**self.result)
if not arg_amodule.params['id']:
if arg_amodule.params['id'] is None:
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'],
arg_amodule.params['account_id'])
@@ -114,10 +109,23 @@ class decort_k8s(DecortController):
arg_amodule.params['rg_name'])
self.amodule.fail_json(**self.result)
# fail the module - exit
#validate k8ci ID
self.rg_id = validated_rg_id
arg_amodule.params['rg_id'] = validated_rg_id
arg_amodule.params['rg_name'] = validated_rg_facts['name']
self.acc_id = validated_rg_facts['accountId']
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=arg_amodule.params['id'],
k8s_name=arg_amodule.params['name'],
rg_id=validated_rg_id,
check_state=False)
if self.k8s_id:
self.k8s_should_exist = True
self.acc_id = self.k8s_info['accountId']
# check workers and groups for add or remove?
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
@@ -125,22 +133,12 @@ class decort_k8s(DecortController):
self.result['msg'] = "Cannot find K8CI ID {}.".format(arg_amodule.params['k8ci_id'])
self.amodule.fail_json(**self.result)
self.rg_id = validated_rg_id
arg_amodule.params['rg_id'] = validated_rg_id
arg_amodule.params['rg_name'] = validated_rg_facts['name']
self.acc_id = validated_rg_facts['accountId']
arg_amodule.params['k8ci_id'] = validated_k8ci_id
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)
self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=arg_amodule.params['id'],
k8s_name=arg_amodule.params['name'],
rg_id=validated_rg_id,
check_state=False)
if self.k8s_id:
self.k8s_should_exist = True
self.acc_id = self.k8s_info['accountId']
# check workers and groups for add or remove?
return
def package_facts(self,check_mode=False):
@@ -169,7 +167,7 @@ class decort_k8s(DecortController):
ret_dict['name'] = self.k8s_info['name']
ret_dict['techStatus'] = self.k8s_info['techStatus']
ret_dict['state'] = self.k8s_info['status']
ret_dict['rg_id'] = self.rg_id
ret_dict['rg_id'] = self.k8s_info['rgId']
ret_dict['vins_id'] = self.k8s_vins_id
ret_dict['account_id'] = self.acc_id
ret_dict['k8s_Masters'] = self.k8s_info['k8sGroups']['masters']
@@ -266,15 +264,13 @@ class decort_k8s(DecortController):
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_id,self.k8s_info = self.k8s_find(k8s_id=self.amodule.params['id'],
k8s_name=self.amodule.params['name'],
rg_id=self.rg_id,
check_state=False)
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
self.k8s_workers_modify(self.k8s_info,self.amodule.params['workers'])
if self.aparams['workers'] is not None:
self.k8s_workers_modify(self.k8s_info, self.aparams['workers'])
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
@@ -319,12 +315,12 @@ class decort_k8s(DecortController):
required=False,
fallback=(env_fallback, ['DECORT_USER'])),
name=dict(type='str', required=False, default=""),
id=dict(type='int', required=False, default=0),
id=dict(type='int', required=False),
getConfig=dict(type='bool',required=False, default=False),
rg_id=dict(type='int', default=0),
rg_name=dict(type='str',default=""),
vins_id=dict(type='int', required=False,default=None),
k8ci_id=dict(type='int', required=True),
k8ci_id=dict(type='int'),
network_plugin=dict(type='str',required=False,default="flannel"),
master_count=dict(type='int', default=1),
master_cpu=dict(type='int', default=2),
@@ -332,7 +328,49 @@ class decort_k8s(DecortController):
master_disk=dict(type='int', default=10),
master_sepid=dict(type='int', required=False, default=None),
master_pool=dict(type='str', required=False, default=None),
workers=dict(type='list',required=True),
workers=dict(
type='list',
elements='dict',
options=dict(
name=dict(
type='str',
required=True,
),
num=dict(
type='int',
),
cpu=dict(
type='int',
),
ram=dict(
type='int',
),
disk=dict(
type='int',
),
sep_id=dict(
type='int',
),
pool=dict(
type='str',
),
annotations=dict(
type='list',
elements='str',
),
ci_user_data=dict(
type='dict',
),
labels=dict(
type='list',
elements='str',
),
taints=dict(
type='list',
elements='str',
),
),
),
workers_metadata=dict(type='bool',required=False,default=False),
extnet_id=dict(type='int', default=0),
description=dict(type='str', default="Created by decort ansible module"),
@@ -366,7 +404,6 @@ def main():
],
required_one_of=[
['id', 'name'],
['rg_id','rg_name']
],
)

View File

@@ -69,11 +69,11 @@ options:
required: no
arch:
description:
- Architecture of the KVM VM. DECORT supports KVM hosts based on Intel x86 and IBM PowerPC hardware.
- Architecture of the KVM VM. DECORT supports KVM hosts based on Intel x86.
- This parameter is used when new KVM VM is created and ignored for all other operations.
- Module may fail if your DECORT installation does not have physical nodes of specified architecture.
default: X86_64
choices: [ X86_64, PPC64_LE ]
choices: [ X86_64 ]
required: yes
authenticator:
description:
@@ -355,6 +355,8 @@ class decort_kvmvm(DecortController):
# call superclass constructor first
super(decort_kvmvm, self).__init__(arg_amodule)
self.check_amodule_args()
self.comp_should_exist = False
# This following flag is used to avoid extra (and unnecessary) get of compute details prior to
# packaging facts before the module completes. As ""
@@ -415,19 +417,59 @@ class decort_kvmvm(DecortController):
check_state=False)
if self.comp_id:
if self.comp_info['status'] != 'DESTROYED' and self.comp_info['arch'] not in ["X86_64", "PPC64_LE"]:
# If we found a Compute in a non-DESTROYED state and it is not of type valid arch, abort the module
self.result['failed'] = True
self.result['msg'] = ("Compute ID {} architecture '{}' is not supported by "
"decort_kvmvm module.").format(self.comp_id,
self.amodule.params['arch'])
self.amodule.fail_json(**self.result)
# fail the module - exit
self.comp_should_exist = True
self.acc_id = self.comp_info['accountId']
return
def check_amodule_args(self):
"""
Additional Ansible Module arguments validation that
cannot be implemented using Ansible Argument spec.
"""
# Check parameter "networks" for DPDK type
aparam_nets = self.aparams['networks']
if aparam_nets:
net_types = {net['type'] for net in aparam_nets}
DPDK = 'DPDK'
if DPDK in net_types and not net_types.issubset({'DPDK', 'EMPTY'}):
self.message(
'Check for parameter "networks" failed: a compute cannot'
' be connected to a DPDK network and a network of another'
' type at the same time.'
)
self.exit(fail=True)
# Check for unacceptable parameters for a blank Compute
if (
self.aparams['image_id'] is None
and self.aparams['image_name'] is None
):
for parameter in (
'ssh_key',
'ssh_key_user',
'ci_user_data',
):
if self.aparams[parameter] is not None:
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_disk'] is None
):
self.message(
'Check for parameter "sep_id" failed: '
'"image_id" or "image_name" or "boot_disk" '
'must be specified to set sep_id.'
)
self.exit(fail=True)
def nop(self):
"""No operation (NOP) handler for Compute management by decort_kvmvm module.
This function is intended to be called from the main switch construct of the module
@@ -475,42 +517,45 @@ 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')
validated_bdisk_size = self.amodule.params['boot_disk'] or 0
if self.amodule.params['arch'] not in ["X86_64", "PPC64_LE"]:
self.result['failed'] = True
self.result['msg'] = ("Unsupported architecture '{}' is specified for "
"KVM VM create.").format(self.amodule.params['arch'])
self.amodule.fail_json(**self.result)
# fail the module - exit
validated_bdisk_size = 0
image_facts = None
# either image_name or image_id must be present
if self.check_amodule_argument('image_id', abort=False) and self.amodule.params['image_id'] > 0 :
# find image by image ID and account ID
# image_find(self, image_id, image_name, account_id, rg_id=0, sepid=0, pool=""):
_, image_facts = self.image_find(image_id=self.amodule.params['image_id'],
image_name="",
account_id=self.acc_id)
elif self.check_amodule_argument('image_name', abort=False) and self.amodule.params['image_name'] != "":
# find image by image name and account ID
_, image_facts = self.image_find(image_id=0,
image_name=self.amodule.params['image_name'],
account_id=self.acc_id)
image_id, image_facts = None, None
if (
self.amodule.params['image_id'] is None
and self.amodule.params['image_name'] is None
):
if self.amodule.params['state'] not in ('poweredoff', 'halted'):
self.result['msg'] = (
'"state" parameter for a blank Compute must be either '
'"poweredoff" or "halted".'
)
self.exit(fail=True)
else:
# neither image_name nor image_id are set - abort the script
self.result['failed'] = True
self.result['msg'] = "Missing both 'image_name' and 'image_id'. You need to specify one to create a Compute."
self.amodule.fail_json(**self.result)
# fail the module - exit
# either image_name or image_id must be present
if (
self.check_amodule_argument('image_id', abort=False)
and self.amodule.params['image_id'] > 0
):
# find image by image ID and account ID
# image_find(self, image_id, image_name, account_id, rg_id=0, sepid=0, pool=""):
image_id, image_facts = self.image_find(
image_id=self.amodule.params['image_id'],
image_name="",
account_id=self.acc_id)
elif (
self.check_amodule_argument('image_name', abort=False)
and self.amodule.params['image_name'] != ""
):
# find image by image name and account ID
image_id, image_facts = self.image_find(
image_id=0,
image_name=self.amodule.params['image_name'],
account_id=self.acc_id,
)
if ((not self.check_amodule_argument('boot_disk', False)) or
self.amodule.params['boot_disk'] <= image_facts['size']):
# adjust disk size to the minimum allowed by OS image, which will be used to spin off this Compute
validated_bdisk_size = image_facts['size']
else:
validated_bdisk_size =self.amodule.params['boot_disk']
if validated_bdisk_size <= image_facts['size']:
# adjust disk size to the minimum allowed by OS image, which will be used to spin off this Compute
validated_bdisk_size = image_facts['size']
# NOTE: due to a libvirt "feature", that impacts management of a VM created without any network interfaces,
# we create KVM VM in HALTED state.
@@ -536,17 +581,24 @@ class decort_kvmvm(DecortController):
cloud_init_params = None
# if we get through here, all parameters required to create new Compute instance should be at hand
match self.amodule.params['chipset'].lower():
case 'q35':
chipset = 'Q35'
case 'i440fx':
chipset = 'i440fx'
# NOTE: KVM VM is created in HALTED state and must be explicitly started
self.comp_id = self.kvmvm_provision(rg_id=self.rg_id,
comp_name=self.amodule.params['name'], arch=self.amodule.params['arch'],
comp_name=self.amodule.params['name'],
cpu=self.amodule.params['cpu'], ram=self.amodule.params['ram'],
boot_disk=validated_bdisk_size,
image_id=image_facts['id'],
image_id=image_id,
annotation=self.amodule.params['annotation'],
userdata=cloud_init_params,
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)
start_on_create=start_compute,
chipset=chipset)
self.comp_should_exist = True
# Originally we would have had to re-read comp_info after VM was provisioned
@@ -574,9 +626,17 @@ class decort_kvmvm(DecortController):
# Compute was created
#
# Setup network connections
self.compute_networks(self.comp_info, self.amodule.params['networks'])
if self.amodule.params['networks'] is not None:
self.compute_networks(
comp_dict=self.comp_info,
new_networks=self.amodule.params['networks'],
)
# Next manage data disks
self.compute_data_disks(self.comp_info, self.amodule.params['data_disks'])
if self.amodule.params['data_disks'] is not None:
self.compute_data_disks(
comp_dict=self.comp_info,
new_data_disks=self.amodule.params['data_disks'],
)
self.compute_affinity(self.comp_info,
self.amodule.params['tag'],
@@ -623,25 +683,49 @@ class decort_kvmvm(DecortController):
Note that it does not modify power state of KVM VM.
"""
self.compute_networks(self.comp_info, self.amodule.params['networks'])
if self.amodule.params['networks'] is not None:
self.compute_networks(
comp_dict=self.comp_info,
new_networks=self.aparams['networks'],
order_changing=self.aparams['network_order_changing'],
)
boot_disk_new_size = self.amodule.params['boot_disk']
if boot_disk_new_size:
self.compute_bootdisk_size(self.comp_info, boot_disk_new_size)
self.compute_data_disks(self.comp_info, self.amodule.params['data_disks'])
if self.amodule.params['data_disks'] is not None:
self.compute_data_disks(self.comp_info, self.amodule.params['data_disks'])
self.compute_resize(self.comp_info,
self.amodule.params['cpu'], self.amodule.params['ram'],
wait_for_state_change=arg_wait_cycles)
self.compute_affinity(self.comp_info,
self.amodule.params['tag'],
self.amodule.params['aff_rule'],
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,
)
return
@property
def compute_update_args(self) -> dict:
result_args = {}
aparam_name = self.amodule.params['name']
if aparam_name is not None and aparam_name != self.comp_info['name']:
result_args['name'] = aparam_name
return result_args
def package_facts(self, check_mode=False):
"""Package a dictionary of KVM VM facts according to the decort_kvmvm module specification.
This dictionary will be returned to the upstream Ansible engine at the completion of decort_kvmvm
@@ -670,6 +754,8 @@ class decort_kvmvm(DecortController):
private_ips=[], # IPs on ViNSes; usually, at least one IP is listed
nat_ip="", # IP of the external ViNS interface; can be empty.
tags={},
chipset="",
interfaces=[],
)
if check_mode or self.comp_info is None:
@@ -722,6 +808,10 @@ class decort_kvmvm(DecortController):
# if it is a data disk - append its ID to the list of data disks IDs
ret_dict['data_disks'].append(ddisk['id'])
ret_dict['chipset'] = self.comp_info['chipset']
ret_dict['interfaces'] = self.comp_info['interfaces']
return ret_dict
@staticmethod
@@ -745,7 +835,6 @@ class decort_kvmvm(DecortController):
required=False,
fallback=(env_fallback, ['DECORT_APP_SECRET']),
no_log=True),
arch=dict(type='str', choices=['X86_64', 'PPC64_LE'], default='X86_64'),
authenticator=dict(type='str',
required=True,
choices=['legacy', 'oauth2', 'jwt']),
@@ -756,7 +845,7 @@ class decort_kvmvm(DecortController):
# count=dict(type='int', required=False, default=1),
cpu=dict(type='int', required=False),
# datacenter=dict(type='str', required=False, default=''),
data_disks=dict(type='list', default=[], required=False), # list of integer disk IDs
data_disks=dict(type='list', required=False), # list of integer disk IDs
id=dict(type='int', required=False, default=0),
image_id=dict(type='int', required=False),
image_name=dict(type='str', required=False),
@@ -765,7 +854,39 @@ class decort_kvmvm(DecortController):
fallback=(env_fallback, ['DECORT_JWT']),
no_log=True),
name=dict(type='str'),
networks=dict(type='list', default=[], required=False), # list of dictionaries
networks=dict(
type='list',
elements='dict',
options=dict(
type=dict(
type='str',
required=True,
choices=[
'VINS',
'EXTNET',
'VFNIC',
'DPDK',
'EMPTY',
],
),
id=dict(
type='int',
),
ip_addr=dict(
type='str',
),
),
required_if=[
('type', 'VINS', ('id',)),
('type', 'EXTNET', ('id',)),
('type', 'VFNIC', ('id',)),
('type', 'DPDK', ('id',)),
],
),
network_order_changing=dict(
type='bool',
default=False,
),
oauth2_url=dict(type='str',
required=False,
fallback=(env_fallback, ['DECORT_OAUTH2_URL'])),
@@ -794,6 +915,11 @@ class decort_kvmvm(DecortController):
# wait_for_ip_address=dict(type='bool', required=False, default=False),
workflow_callback=dict(type='str', required=False),
workflow_context=dict(type='str', required=False),
chipset=dict(
type='str',
default='i440fx',
choices=['Q35', 'q35', 'I440FX', 'i440fx']
),
)
# Workflow digest:

View File

@@ -46,7 +46,7 @@ class decort_lb(DecortController):
"rise": 2,
"slowstart": 60000,
"weight": 100,
}
}
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:
@@ -54,7 +54,6 @@ class decort_lb(DecortController):
self.result['msg'] = "Specified LB ID {} not found."\
.format(arg_amodule.params['lb _id'])
self.fail_json(**self.result)
self.acc_id = self.lb_facts['accountId']
self.rg_id = self.lb_facts['rgId']
self.vins_id = self.lb_facts['vinsId']
return
@@ -105,7 +104,7 @@ class decort_lb(DecortController):
)
self.amodule.fail_json(**self.result)
if self.rg_id and self.vins_id:
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)
return
@@ -274,11 +273,11 @@ class decort_lb(DecortController):
vins_id=dict(type='int', required=False, default=0),
verify_ssl=dict(type='bool', required=False, default=True),
lb_id=dict(type='int', required=False, default=0),
lb_name=dict(type='str', required=True),
lb_name=dict(type='str'),
ha_lb=dict(type='bool', required=False, default=False),
backends=dict(type='list',required=False,default=[]),
frontends=dict(type='list',required=False,default=[]),
servers=dict(type='list',required=False,default=[]),
backends=dict(type='list',required=False),
frontends=dict(type='list',required=False),
servers=dict(type='list',required=False),
permanently=dict(type='bool', required=False, default=False),
workflow_callback=dict(type='str', required=False),
workflow_context=dict(type='str', required=False),

View File

@@ -156,7 +156,7 @@ options:
required: no
architecture:
description:
- 'Binary architecture of the image. Note. `X86_64` or `PPC64_LE`. Used when creating
- 'Binary architecture of the image. Note. `X86_64`. Used when creating
-an operating system image.'
required: no
imagetype:
@@ -208,10 +208,6 @@ options:
- 'The password for loading the binary media. Used in conjunction with `usernameDL`. Used when creating
- an operating system image.'
required: no
permanently:
description:
- 'Whether to permanently delete the image. Used when deleting an image. The default is false.'
required: no
'''
@@ -363,29 +359,28 @@ class decort_osimage(DecortController):
passwordDL=amodule.params['passwordDL'],
sepId=amodule.params['sepId'],
poolName=amodule.params['poolName'],
architecture=amodule.params['architecture'],
drivers=amodule.params['drivers'])
self.result['changed'] = True
return image_facts
def decort_virt_image_link(self,amodule):
# function that links an OS image to a virtual one
self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.validated_image_id)
self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.target_image_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)
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.validated_image_id,
self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.target_image_id,
decort_osimage.decort_osimage_package_facts(image_facts)['id'],)
return image_id, image_facts
def decort_image_delete(self,amodule):
# function that removes an image
self.image_delete(imageId=amodule.image_id_delete, permanently=amodule.params['permanently'])
self.image_delete(imageId=amodule.image_id_delete)
self.result['changed'] = True
self.result['msg'] = ("Image '{}' deleted").format(amodule.image_id_delete)
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.validated_image_id)
image_facts = self.virt_image_create(name=amodule.params['virt_name'], targetId=self.target_image_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
@@ -487,7 +482,6 @@ class decort_osimage(DecortController):
default='present',
choices=['absent', 'present']),
drivers=dict(type='str', required=False, default="KVM_X86"),
architecture=dict(type='str', required=False, default="X86_64"),
imagetype=dict(type='str', required=False, default="linux"),
boottype=dict(type='str', required=False, default="uefi"),
url=dict(type='str', required=False),
@@ -499,7 +493,6 @@ class decort_osimage(DecortController):
image_password=dict(type='str', required=False),
usernameDL=dict(type='str', required=False),
passwordDL=dict(type='str', required=False),
permanently=dict(type='bool', required=False, default=False),
)
@@ -524,22 +517,25 @@ def main():
if amodule.params['virt_name'] or amodule.params['virt_id']:
image_id, image_facts = decort_osimage.decort_virt_image_find(decon, amodule)
decon.validated_image_id, _ = decort_osimage.decort_image_find(decon, amodule)
if amodule.params['image_name'] or amodule.params['image_id']:
decon.target_image_id, _ = decort_osimage.decort_image_find(decon, amodule)
else:
decon.target_image_id = 0
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] > 0:
decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode)
decon.validated_virt_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
decon.validated_virt_image_name = decort_osimage.decort_osimage_package_facts(image_facts)['name']
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.validated_image_id > 0:
if decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.target_image_id > 0:
image_id, image_facts = decort_osimage.decort_virt_image_create(decon,amodule)
decon.result['msg'] = ("Virtual image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id'])
decon.result['changed'] = True
elif decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.validated_image_id == 0:
elif decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.target_image_id == 0:
decon.result['msg'] = ("Cannot find OS image")
amodule.fail_json(**decon.result)
if decon.validated_virt_image_id:
if decort_osimage.decort_osimage_package_facts(image_facts)['linkto'] != decon.validated_image_id:
if decon.validated_virt_image_id and decon.target_image_id:
if decort_osimage.decort_osimage_package_facts(image_facts)['linkto'] != decon.target_image_id:
decort_osimage.decort_virt_image_link(decon,amodule)
decon.result['changed'] = True
amodule.exit_json(**decon.result)
@@ -564,8 +560,6 @@ def main():
decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id']
elif amodule.params['state'] == "absent" and decon.validated_image_id:
if amodule.params['image_name'] or amodule.params['image_id'] and\
decort_osimage.decort_osimage_package_facts(image_facts)['accountId'] == amodule.params['account_Id']:
amodule.image_id_delete = decon.validated_image_id
decort_osimage.decort_image_delete(decon,amodule)

View File

@@ -248,7 +248,7 @@ def decort_pfw_parameters():
required=False,
fallback=(env_fallback, ['DECORT_PASSWORD']),
no_log=True),
rules=dict(type='list', required=False, default=[]),
rules=dict(type='list', required=False),
state=dict(type='str',
default='present',
choices=['absent', 'present']),
@@ -314,9 +314,11 @@ def main():
if amodule.params['state'] == 'absent':
# ignore amodule.params['rules'] and remove all rules associated with this Compute
pfw_facts = decon.pfw_configure(comp_facts, vins_facts, None)
else:
elif amodule.params['rules'] is not None:
# manage PFW rules accodring to the module arguments
pfw_facts = decon.pfw_configure(comp_facts, vins_facts, amodule.params['rules'])
else:
pfw_facts = decon._pfw_get(comp_facts['id'], vins_facts['id'])
#
# complete module run

View File

@@ -217,31 +217,44 @@ class decort_rg(DecortController):
self.validated_rg_id = 0
self.validated_rg_facts = None
if self.amodule.params['account_id']:
self.validated_acc_id, _ = self.account_find("", amodule.params['account_id'])
elif amodule.params['account_name']:
self.validated_acc_id, _ = self.account_find(amodule.params['account_name'])
if not self.validated_acc_id:
# we failed to locate account by either name or ID - abort with an error
self.result['failed'] = True
self.result['msg'] = ("Current user does not have access to the requested account "
"or non-existent account specified.")
self.amodule.fail_json(**self.result)
if amodule.params['rg_id'] > 0:
self.validated_rg_id = amodule.params['rg_id']
if amodule.params['rg_id'] is None:
if self.amodule.params['account_id']:
self.validated_acc_id, _ = self.account_find("", amodule.params['account_id'])
elif amodule.params['account_name']:
self.validated_acc_id, _ = self.account_find(amodule.params['account_name'])
if not self.validated_acc_id:
# we failed to locate account by either name or ID - abort with an error
self.result['failed'] = True
self.result['msg'] = ("Current user does not have access to the requested account "
"or non-existent account specified.")
self.amodule.fail_json(**self.result)
# Check if the RG with the specified parameters already exists
self.validated_rg_id, self.rg_facts = self.rg_find(self.validated_acc_id,
arg_rg_id = self.validated_rg_id,
arg_rg_name=amodule.params['rg_name'],
arg_check_state=False)
self.get_info()
if amodule.params['state'] != "absent":
self.rg_should_exist = True
else:
self.rg_should_exist = False
def get_info(self):
# If this is the first getting info
if not self.validated_rg_id:
self.validated_rg_id, self.rg_facts = self.rg_find(
arg_account_id=self.validated_acc_id,
arg_rg_id=self.aparams['rg_id'],
arg_rg_name=self.aparams['rg_name'],
arg_check_state=False,
)
# If this is a repeated getting info
else:
# If check mode is enabled, there is no needed to
# request info again
if self.amodule.check_mode:
return
_, self.rg_facts = self.rg_find(arg_rg_id=self.validated_rg_id)
def access(self):
should_change_access = False
acc_granted = False
@@ -296,17 +309,37 @@ class decort_rg(DecortController):
self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota)
self.result['failed'] = True
if self.result['failed'] != True:
self.rg_update(self.rg_facts, self.amodule.params['quotas'],
self.amodule.params['resType'], self.amodule.params['rename'])
if not self.result['failed']:
self.rg_update(
arg_rg_dict=self.rg_facts,
arg_quotas=self.amodule.params['quotas'],
arg_res_types=self.amodule.params['resType'],
arg_newname=self.amodule.params['rename'],
arg_sep_pools=self.amodule.params['sep_pools'],
)
self.rg_should_exist = True
return
def setDefNet(self):
if self.amodule.params['def_netId'] != self.rg_facts['def_net_id']:
self.rg_setDefNet(self.validated_rg_id,
self.amodule.params['def_netType'],
self.amodule.params['def_netId'])
rg_def_net_type = self.rg_facts['def_net_type']
rg_def_net_id = self.rg_facts['def_net_id']
aparam_def_net_type = self.aparams['def_netType']
aparam_def_net_id = self.aparams['def_netId']
need_to_reset = (aparam_def_net_type == 'NONE'
and rg_def_net_type != aparam_def_net_type)
need_to_change = False
if aparam_def_net_id is not None:
need_to_change = (aparam_def_net_id != rg_def_net_id
or aparam_def_net_type != rg_def_net_type)
if need_to_reset or need_to_change:
self.rg_setDefNet(
arg_rg_id=self.validated_rg_id,
arg_net_type=aparam_def_net_type,
arg_net_id=aparam_def_net_id,
)
self.rg_should_exist = True
return
@@ -351,8 +384,11 @@ class decort_rg(DecortController):
return
def destroy(self):
self.rg_delete(self.validated_rg_id, self.amodule.params['permanently'])
self.rg_delete(
rg_id=self.validated_rg_id,
permanently=self.amodule.params['permanently'],
recursively=self.aparams['recursive_deletion'],
)
if self.amodule.params['permanently'] == True:
self.rg_facts['status'] = 'DESTROYED'
else:
@@ -393,6 +429,7 @@ class decort_rg(DecortController):
ret_dict['defNetType'] = self.rg_facts['def_net_type']
ret_dict['ViNS'] = self.rg_facts['vins']
ret_dict['computes'] = self.rg_facts['vms']
ret_dict['uniqPools'] = self.rg_facts['uniqPools']
return ret_dict
@@ -418,7 +455,7 @@ class decort_rg(DecortController):
controller_url=dict(type='str', required=True),
# datacenter=dict(type='str', required=False, default=''),
def_netType=dict(type='str', choices=['PRIVATE','PUBLIC', 'NONE'], default='PRIVATE'),
def_netId=dict(type='int', default=0),
def_netId=dict(type='int'),
extNetId=dict(type='int', default=0),
extNetIp=dict(type='str', default=""),
owner=dict(type='str', default=""),
@@ -446,10 +483,29 @@ class decort_rg(DecortController):
required=False,
fallback=(env_fallback, ['DECORT_USER'])),
rg_name=dict(type='str', required=False,),
rg_id=dict(type='int', required=False, default=0),
rg_id=dict(type='int', required=False),
verify_ssl=dict(type='bool', required=False, default=True),
workflow_callback=dict(type='str', required=False),
workflow_context=dict(type='str', required=False),
sep_pools=dict(
type='list',
elements='dict',
options=dict(
sep_id=dict(
type='int',
required=True,
),
pool_names=dict(
type='list',
required=True,
elements='str',
),
),
),
recursive_deletion=dict(
type='bool',
default=False,
)
)
# Workflow digest:
@@ -486,11 +542,16 @@ def main():
elif amodule.params['state'] == "disabled":
decon.enable()
if amodule.params['state'] in ['present', 'enabled']:
if amodule.params['quotas'] or amodule.params['resType'] or amodule.params['rename'] != "":
if (
amodule.params['quotas']
or amodule.params['resType']
or amodule.params['rename'] != ""
or amodule.params['sep_pools'] is not None
):
decon.update()
if amodule.params['access']:
decon.access()
if amodule.params['def_netId'] > 0:
if amodule.params['def_netType'] is not None:
decon.setDefNet()
elif decon.rg_facts['status'] == "DELETED":
@@ -528,6 +589,8 @@ def main():
amodule.fail_json(**decon.result)
else:
if decon.rg_should_exist:
if decon.result['changed']:
decon.get_info()
decon.result['facts'] = decon.package_facts(amodule.check_mode)
amodule.exit_json(**decon.result)
else:

View File

@@ -112,7 +112,15 @@ class DecortUserInfo(DecortController):
type='str',
),
status_code=dict(
type='str',
type='dict',
options=dict(
min=dict(
type='int',
),
max=dict(
type='int',
),
),
),
time=dict(
type='dict',
@@ -151,6 +159,7 @@ class DecortUserInfo(DecortController):
),
pagination=dict(
type='dict',
apply_defaults=True,
options=dict(
number=dict(
type='int',
@@ -158,7 +167,7 @@ class DecortUserInfo(DecortController):
),
size=dict(
type='int',
required=True,
default=50,
),
),
),
@@ -276,7 +285,13 @@ class DecortUserInfo(DecortController):
input_args_filter = input_args['filter']
if input_args_filter:
mapped_args['api_method'] = input_args_filter['api_method']
mapped_args['http_status_code'] = input_args_filter['status_code']
match input_args_filter['status_code']:
case {'min': int() as min_status_code}:
mapped_args['min_status_code'] = min_status_code
match input_args_filter['status_code']:
case {'max': int() as max_status_code}:
mapped_args['max_status_code'] = max_status_code
match input_args_filter['time']:
case {'start': {'unix': int() as start_unix_time}}: