From e5edf40b6e5b9cb2165a72729f02e79e9c7b2156 Mon Sep 17 00:00:00 2001 From: Sergey Shubin svs1370 Date: Fri, 29 May 2020 20:02:40 +0300 Subject: [PATCH] Fixing initial bugs in Disk and KvmVM modules, adjusting utility module accordingly --- library/decort_disk.py | 71 ++++++++++++++++++++++-------------- library/decort_kvmvm.py | 7 +++- module_utils/decort_utils.py | 24 ++++++------ 3 files changed, 61 insertions(+), 41 deletions(-) diff --git a/library/decort_disk.py b/library/decort_disk.py index d8b1749..da76073 100644 --- a/library/decort_disk.py +++ b/library/decort_disk.py @@ -78,20 +78,20 @@ options: - URL of the DECORT controller that will be contacted to manage the RG according to the specification. - 'This parameter is always required regardless of the specified I(authenticator) type.' required: yes - disk_id: + id: description: - - `ID of the disk to manage. If I(disk_id) is specified it is assumed, that this disk already - exists. In other words, you cannot create new disk by specifying its ID, use I(disk_name) + - `ID of the disk to manage. If I(id) is specified it is assumed, that this disk already + exists. In other words, you cannot create new disk by specifying its ID, use I(name) when creating new disk.` - - `If non-zero I(disk_id) is specified, then I(disk_name), I(account_id) and I(account_name) + - `If non-zero I(id) is specified, then I(name), I(account_id) and I(account_name) are ignored.` default: 0 required: no - disk_name: + name: description: - `Name of the disk to manage. To manage disk by name you also need to specify either I(account_id) or I(account_name).` - - If non-zero I(disk_id) is specified, I(disk_name) is ignored. + - If non-zero I(id) is specified, I(name) is ignored. - `Note that the platform does not enforce uniqueness of disk names, so if more than one disk with this name exists under the specified account, module will return the first occurence.` @@ -206,7 +206,7 @@ EXAMPLES = ''' app_id: "{{ MY_APP_ID }}" app_secret: "{{ MY_APP_SECRET }}" controller_url: "https://cloud.digitalenergy.online" - disk_name: "MyDataDisk01" + name: "MyDataDisk01" sep_id: 1 pool: "default" size: 50 @@ -288,7 +288,7 @@ def decort_disk_parameters(): return dict( account_id=dict(type='int', required=False, default=0), account_name=dict(type='str', required=False, default=''), - annotation=dict(type='str', required=False, default=''), + annotation=dict(type='str', required=False, default='Disk by decort_disk'), app_id=dict(type='str', required=False, fallback=(env_fallback, ['DECORT_APP_ID'])), @@ -300,8 +300,8 @@ def decort_disk_parameters(): required=True, choices=['legacy', 'oauth2', 'jwt']), controller_url=dict(type='str', required=True), - disk_id=dict(type='int', required=False, default=0), - disk_name=dict(type='str', required=False), + id=dict(type='int', required=False, default=0), + name=dict(type='str', required=False), force_detach=dict(type='bool', required=False, default=False), jwt=dict(type='str', required=False, @@ -352,18 +352,18 @@ def main(): validated_acc_id = 0 acc_facts = None # will hold Account facts - if amodule.params['disk_id']: + if amodule.params['id']: # expect existing Disk with the specified ID # This call to disk_find will abort the module if no Disk with such ID is present - disk_id, disk_facts = decon.disk_find(amodule.params['disk_id']) + disk_id, disk_facts = decon.disk_find(amodule.params['id']) if not disk_id: decon.result['failed'] = True - decon.result['msg'] = "Specified Disk ID {} not found.".format(amodule.params['disk_id']) + decon.result['msg'] = "Specified Disk ID {} not found.".format(amodule.params['id']) amodule.fail_json(**decon.result) validated_acc_id =disk_facts['accountId'] - elif (amodule.params['account_id'] or amodule.params['account_name'] != "") and amodule.params['disk_name'] != "": + elif (amodule.params['account_id'] or amodule.params['account_name'] != "") and amodule.params['name'] != "": # Make sure disk name is specified, if not - fail the module - if amodule.params['disk_name'] == "": + if amodule.params['name'] == "": decon.result['failed'] = True decon.result['msg'] = ("Cannot manage disk if both ID is 0 and disk name is empty.") amodule.fail_json(**decon.result) @@ -375,7 +375,7 @@ def main(): "or non-existent account specified.") amodule.fail_json(**decon.result) # This call to disk_find may return disk_id=0 if no Disk with this name found in - disk_id, disk_facts = decon.disk_find(disk_id=0, disk_name=amodule.params['disk_name'], + disk_id, disk_facts = decon.disk_find(disk_id=0, disk_name=amodule.params['name'], account_id=validated_acc_id, check_state=False) else: @@ -384,7 +384,7 @@ def main(): decon.result['failed'] = True if amodule.params['account_id'] == 0 and amodule.params['account_name'] == "": decon.result['msg'] = "Cannot find Disk by name when account name is empty and account ID is 0." - if amodule.params['disk_name'] == "": + if amodule.params['name'] == "": decon.result['msg'] = "Cannot find Disk by empty name." amodule.fail_json(**decon.result) @@ -400,6 +400,8 @@ def main(): # disk_should_exist = False + target_sep_id = 0 + target_pool = "default" if disk_id: disk_should_exist = True @@ -451,8 +453,8 @@ def main(): elif decon.check_amodule_argument('place_with', False) and amodule.params['place_with'] > 0: # request to place this disk on the same SEP as the specified OS image # validate specified OS image and assign SEP ID accordingly - image_id, image_facts = decon.image_find() - pass + image_id, image_facts = decon.image_find(amodule.params['place_with'], "", 0) + target_sep_id = image_facts['sepid'] else: # no new SEP ID is explicitly specified, and no place_with option - use sep_id from the disk_facts target_sep_id = disk_facts['sepid'] @@ -480,14 +482,29 @@ def main(): decon.result['failed'] = False decon.result['changed'] = False decon.result['msg'] = ("Nothing to do as target state 'absent' was requested for " - "non-existent Disk name '{}'").format(amodule.params['disk_name']) + "non-existent Disk name '{}'").format(amodule.params['name']) elif amodule.params['state'] == 'present': - decon.check_amodule_argument('disk_name') - # as we already have account ID, we can create Disk and get disk_id on success - # - # TODO: implement SEP ID selction logic - # - disk_id = decon.disk_provision(disk_name=disk_facts['name'], # as this disk was found, its name is in the facts + decon.check_amodule_argument('name') # if disk name not specified, fail the module + decon.check_amodule_argument('size') # if disk size not specified, fail the module + + # as we already have account ID, we can create Disk and get disk id on success + if decon.check_amodule_argument('sep_id', False) and amodule.params['sep_id'] > 0: + # non-zero sep_id is explicitly passed in module arguments + target_sep_id = amodule.params['sep_id'] + elif decon.check_amodule_argument('place_with', False) and amodule.params['place_with'] > 0: + # request to place this disk on the same SEP as the specified OS image + # validate specified OS image and assign SEP ID accordingly + image_id, image_facts = decon.image_find(amodule.params['place_with'], "", 0) + target_sep_id = image_facts['sepid'] + else: + # no SEP ID is explicitly specified, and no place_with option - we do not know where + # to place the new disk - fail the module + decon.result['failed'] = True + decon.result['msg'] = ("Cannot create new Disk name '{}': no SEP ID specified and " + "no 'place_with' option used.").format(amodule.params['name']) + amodule.fail_json(**decon.result) + + disk_id = decon.disk_provision(disk_name=amodule.params['name'], size=amodule.params['size'], account_id=validated_acc_id, sep_id=target_sep_id, @@ -500,7 +517,7 @@ def main(): decon.result['changed'] = False decon.result['msg'] = ("Invalid target state '{}' requested for non-existent " "Disk name '{}'").format(amodule.params['state'], - amodule.params['disk_name']) + amodule.params['name']) # # conditional switch end - complete module run diff --git a/library/decort_kvmvm.py b/library/decort_kvmvm.py index 56856e0..8a210d1 100644 --- a/library/decort_kvmvm.py +++ b/library/decort_kvmvm.py @@ -105,6 +105,11 @@ options: it accordingly. Note that resize operation on a running VM may generate errors as not all OS images support hot resize feature.' required: no + data_disks: + description: + - Optional list of integer IDs of the pre-existing disks that will be attached to this VM. + - These are additional disks (aka data disks) besides boot disk, which is created and attached automatically. + required: no id: description: - ID of the KVM VM to manage. @@ -542,7 +547,7 @@ class decort_kvmvm(DecortController): # if we get through here, all parameters required to create new Compute instance should be at hand - self.comp_id = self.compute_provision(rg_id=self.rg_id, + self.comp_id = self.kvmvm_provision(rg_id=self.rg_id, comp_name=self.amodule.params['name'], arch=self.amodule.params['arch'], cpu=self.amodule.params['cpu'], ram=self.amodule.params['ram'], boot_disk=validated_bdisk_size, diff --git a/module_utils/decort_utils.py b/module_utils/decort_utils.py index ffab63b..9f270c6 100644 --- a/module_utils/decort_utils.py +++ b/module_utils/decort_utils.py @@ -763,14 +763,16 @@ class DecortController(object): api_params = dict(rgId=rg_id, name=comp_name, - description=annotation, - vcpus=cpu, memory=ram, + cpu=cpu, ram=ram, imageId=image_id, - disksize=boot_disk, - start_machine=start_on_create) # start_machine parameter requires DECORT API ver 3.3.1 or higher + bootDisk=boot_disk, + start=start_on_create) # start_machine parameter requires DECORT API ver 3.3.1 or higher if userdata: api_params['userdata'] = json.dumps(userdata) # we need to pass a string object as "userdata" + if annotation: + api_params['decs'] = annotation + 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. self.result['failed'] = False @@ -1149,7 +1151,7 @@ class DecortController(object): self.result['failed'] = True self.result['msg'] = ("Failed to find RG ID {}, and account ID is zero.").format(rg_id) return 0, None - validated_acc_id = rg_facts['accountId'] + validated_acc_id = rg_facts['accountId'] api_params = dict(accountId=validated_acc_id) api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/images/list", api_params) @@ -2212,17 +2214,10 @@ class DecortController(object): return 0, None elif disk_name != "": if account_id > 0: - # TODO: in the absense of disks/list or disks/search API call it is not possible to - # fully implement this method - # - self.result['failed'] = True - self.result['msg'] = "disk_find(): looking up disk by name and account ID not implemented." - self.amodule.fail_json(**self.result) - # api_params = dict(accountId=account_id, name=disk_name, showAll=False) # we do not want to see disks in DESTROYED, PURGED or invalid states - api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/search", api_params) + api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/list", api_params) # the above call may return more than one matching disk disks_list = json.loads(api_resp.content.decode('utf8')) for runner in disks_list: @@ -2287,6 +2282,9 @@ class DecortController(object): if api_resp.status_code == 200: ret_disk_id = json.loads(api_resp.content.decode('utf8')) + self.result['failed'] = False + self.result['changed'] = True + return ret_disk_id def disk_resize(self, disk_facts, new_size):