diff --git a/library/decort_bservice.py b/library/decort_bservice.py index 5e1270e..a7b3ab7 100644 --- a/library/decort_bservice.py +++ b/library/decort_bservice.py @@ -43,8 +43,11 @@ class decort_bservice(DecortController): self.fail_json(**self.result) # fail the module -> exit # now validate RG - validated_rg_id, validated_rg_facts = self.rg_find(validated_acc_id, - arg_amodule.params['rg_id'],) + validated_rg_id, validated_rg_facts = self.rg_find( + arg_account_id=validated_acc_id, + arg_rg_id=arg_amodule.params['rg_id'], + arg_rg_name=arg_amodule.params['rg_name'] + ) if not validated_rg_id: self.result['failed'] = True self.result['changed'] = False @@ -76,7 +79,7 @@ class decort_bservice(DecortController): """ self.result['failed'] = False self.result['changed'] = False - if self.k8s_id: + if self.bservice_id: self.result['msg'] = ("No state change required for B-service ID {} because of its " "current status '{}'.").format(self.bservice_id, self.bservice_info['status']) else: @@ -109,7 +112,7 @@ class decort_bservice(DecortController): ) if self.bservice_id: _, self.bservice_info = self.bservice_get_by_id(self.bservice_id) - self.bservice_state(self.bservice_info,'enabled',self.amodule.params['started']) + self.bservice_state(self.bservice_info,'enabled',self.amodule.params['started']) return def action(self,d_state,started=False): @@ -148,8 +151,7 @@ class decort_bservice(DecortController): ret_dict['state'] = self.bservice_info['status'] ret_dict['rg_id'] = self.bservice_info['rgId'] ret_dict['account_id'] = self.acc_id - ret_dict['groupsName'] = self.bservice_info['groupsName'] - ret_dict['groupsIds'] = self.bservice_info['groups'] + ret_dict['groups'] = self.bservice_info['groups'] return ret_dict @staticmethod def build_parameters(): diff --git a/library/decort_disk.py b/library/decort_disk.py index 0df148b..b4d4078 100644 --- a/library/decort_disk.py +++ b/library/decort_disk.py @@ -276,7 +276,7 @@ class decort_disk(DecortController): 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.fail_json(**self.result) + self.amodule.fail_json(**self.result) else: self.acc_id = validated_acc_id self.acc_info = validated_acc_info @@ -289,7 +289,7 @@ class decort_disk(DecortController): else: self.result['failed'] = True self.result['msg'] = ("Cannot manage Disk when its ID is 0 and name is empty") - self.fail_json(**self.result) + self.amodule.fail_json(**self.result) if arg_amodule.params['place_with']: image_id, image_facts = self.image_find(arg_amodule.params['place_with'], "", 0) @@ -311,10 +311,11 @@ class decort_disk(DecortController): ) #IO tune if self.amodule.params['limitIO']: - self.disk_limitIO(self.amodule.params['limitIO'],self.disk_id) + self.disk_limitIO(disk_id=self.disk_id, + limits=self.amodule.params['limitIO']) #set share status if self.amodule.params['shareable'] and self.amodule.params['type'] == "D": - self.dick_share(self.disk_id,self.amodule.params['shareable']) + self.disk_share(self.disk_id,self.amodule.params['shareable']) return def action(self,restore=False): @@ -324,7 +325,7 @@ class decort_disk(DecortController): self.disk_restore(self.disk_id) #rename if id present if self.amodule.params['name'] != self.disk_info['name']: - self.disk_rename(diskId=self.disk_id, + self.disk_rename(disk_id=self.disk_id, name=self.amodule.params['name']) self.disk_info['name'] = self.amodule.params['name'] #resize @@ -519,6 +520,9 @@ def main(): elif decon.disk_info['status'] == "DELETED": if amodule.params['state'] in ('present'): decon.action(restore=True) + elif (amodule.params['state'] == 'absent' and + amodule.params['permanently']): + decon.delete() else: decon.nop() else: diff --git a/library/decort_group.py b/library/decort_group.py index 8162f0d..9c29ff8 100644 --- a/library/decort_group.py +++ b/library/decort_group.py @@ -132,6 +132,7 @@ class decort_group(DecortController): self.bservice_id, self.group_id ) + self.group_should_exist = False return @@ -206,10 +207,23 @@ class decort_group(DecortController): bservice_id=dict(type='int', required=True), count=dict(type='int', required=True), timeoutStart=dict(type='int', required=False), - role=dict(type='str', required=False), + role=dict(type='str', required=False, default=''), cpu=dict(type='int', required=False), ram=dict(type='int', required=False), - networks=dict(type='list', default=[], required=False), + networks=dict( + type='list', default=[], elements='dict', + options=dict( + type=dict( + type='str', + required=True, + choices=['VINS', 'EXTNET'] + ), + id=dict( + type='int', + required=True + ) + ) + ), description=dict(type='str', default="Created by decort ansible module"), verify_ssl=dict(type='bool', required=False, default=True), workflow_callback=dict(type='str', required=False), diff --git a/library/decort_k8s.py b/library/decort_k8s.py index 13ea565..27d92e9 100644 --- a/library/decort_k8s.py +++ b/library/decort_k8s.py @@ -102,8 +102,11 @@ class decort_k8s(DecortController): self.amodule.fail_json(**self.result) # fail the module -> exit # now validate RG - validated_rg_id, validated_rg_facts = self.rg_find(validated_acc_id, - arg_amodule.params['rg_id'],) + validated_rg_id, validated_rg_facts = self.rg_find( + arg_account_id=validated_acc_id, + arg_rg_id=arg_amodule.params['rg_id'], + arg_rg_name=arg_amodule.params['rg_name'] + ) if not validated_rg_id: self.result['failed'] = True self.result['changed'] = False @@ -150,6 +153,9 @@ class decort_k8s(DecortController): config=None, ) + if self.amodule.params['getConfig'] and self.k8s_info['techStatus'] == "STARTED": + ret_dict['config'] = self.k8s_getConfig() + if check_mode: # in check mode return immediately with the default values return ret_dict @@ -169,9 +175,6 @@ class decort_k8s(DecortController): ret_dict['k8s_Masters'] = self.k8s_info['k8sGroups']['masters'] ret_dict['k8s_Workers'] = self.k8s_info['k8sGroups']['workers'] - if self.amodule.params['getConfig'] and self.k8s_info['techStatus'] == "STARTED": - ret_dict['config'] = self.k8s_getConfig() - return ret_dict def nop(self): @@ -232,10 +235,13 @@ class decort_k8s(DecortController): self.amodule.params['description'], self.amodule.params['extnet_only'], ) - + if not k8s_id: - self.result['failed'] = True - self.amodule.fail_json(**self.result) + if k8s_id == 0: + return + else: + self.result['failed'] = True + self.amodule.fail_json(**self.result) self.k8s_id,self.k8s_info = self.k8s_find(k8s_id=k8s_id, k8s_name=self.amodule.params['name'], @@ -254,8 +260,10 @@ class decort_k8s(DecortController): self.k8s_should_exist = False return - def action(self,disared_state,started=True): - + def action(self, disared_state, started=True, preupdate: bool = False): + if preupdate: + # K8s info updating + 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'], @@ -333,10 +341,10 @@ class decort_k8s(DecortController): extnet_only=dict(type='bool', default=False), additionalSANs=dict(type='list',required=False, default=None), init_conf=dict(type='dict', required=False, default=None), - cluster_conf=dict(type='str', required=False, default=None), - kublet_conf=dict(type='str', required=False, default=None), - kubeproxy_conf=dict(type='str', required=False, default=None), - join_conf=dict(type='str', required=False, default=None), + cluster_conf=dict(type='dict', required=False, default=None), + kublet_conf=dict(type='dict', required=False, default=None), + kubeproxy_conf=dict(type='dict', required=False, default=None), + join_conf=dict(type='dict', required=False, default=None), oidc_cert=dict(type='raw',required=False,default=None), verify_ssl=dict(type='bool', required=False, default=True), workflow_callback=dict(type='str', required=False), @@ -386,9 +394,13 @@ def main(): elif subj.k8s_info['status'] == "DELETED": if amodule.params['state'] in ('disabled', 'enabled', 'present'): subj.k8s_restore(subj.k8s_id) - subj.action(amodule.params['state']) + subj.action(disared_state=amodule.params['state'], + preupdate=True) if amodule.params['state'] == 'absent': - subj.nop() + if amodule.params['permanent']: + subj.destroy() + else: + subj.nop() elif subj.k8s_info['techStatus'] in ("STARTED","STOPPED"): if amodule.params['state'] == 'disabled': subj.action(amodule.params['state']) diff --git a/library/decort_kvmvm.py b/library/decort_kvmvm.py index 5753f51..a0be17a 100644 --- a/library/decort_kvmvm.py +++ b/library/decort_kvmvm.py @@ -624,7 +624,11 @@ 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']) - self.compute_bootdisk_size(self.comp_info, self.amodule.params['boot_disk']) + + 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']) self.compute_resize(self.comp_info, self.amodule.params['cpu'], self.amodule.params['ram'], @@ -778,7 +782,7 @@ class decort_kvmvm(DecortController): affinity_label=dict(type='str', required=False), aff_rule=dict(type='list', required=False), aaff_rule=dict(type='list', required=False), - ci_user_data=dict(type='list',elements='dict', required=False), + ci_user_data=dict(type='dict', required=False), state=dict(type='str', default='present', choices=['absent', 'paused', 'poweredoff', 'halted', 'poweredon', 'present', 'check']), diff --git a/library/decort_lb.py b/library/decort_lb.py index aa2b0c9..042691a 100644 --- a/library/decort_lb.py +++ b/library/decort_lb.py @@ -66,16 +66,9 @@ class decort_lb(DecortController): self.result['msg'] = "Specified RG ID {} not found.".format(arg_amodule.params['vins_id']) self.amodule.fail_json(**self.result) - if arg_amodule.params['vins_id']: - self.vins_id, self.vins_facts = self.vins_find(arg_amodule.params['vins_id']) - if not self.vins_id: - self.result['failed'] = True - self.result['msg'] = "Specified ViNS ID {} not found.".format(arg_amodule.params['vins_id']) - self.amodule.fail_json(**self.result) - elif arg_amodule.params['account_id'] or arg_amodule.params['account_name'] != "": - if arg_amodule.params['rg_name']: + if not arg_amodule.params['rg_name']: self.result['failed'] = True self.result['msg'] = ("RG name must be specified with account present") self.amodule.fail_json(**self.result) @@ -86,8 +79,32 @@ class decort_lb(DecortController): self.result['msg'] = ("Current user does not have access to the requested account " "or non-existent account specified.") self.amodule.fail_json(**self.result) - self.rg_id, self.rg_facts = self.rg_find(self._acc_id,0, arg_rg_name=arg_amodule.params['rg_name']) + self.rg_id, self.rg_facts = self.rg_find(self.acc_id,0, arg_rg_name=arg_amodule.params['rg_name']) + if arg_amodule.params['vins_id']: + self.vins_id, self.vins_facts = self.vins_find( + vins_id=arg_amodule.params['vins_id'] + ) + if not self.vins_id: + self.result['failed'] = True + self.result['msg'] = ( + f'Specified ViNS ID {arg_amodule.params["vins_id"]}' + f' not found' + ) + self.amodule.fail_json(**self.result) + elif arg_amodule.params['vins_name']: + self.vins_id, self.vins_facts = self.vins_find( + vins_id=arg_amodule.params['vins_id'], + vins_name=arg_amodule.params['vins_name'], + rg_id=self.rg_id) + if not self.vins_id: + self.result['failed'] = True + self.result['msg'] = ( + f'Specified ViNS name {arg_amodule.params["vins_name"]}' + f' not found in RG ID {self.rg_id}' + ) + self.amodule.fail_json(**self.result) + if self.rg_id and self.vins_id: self.lb_id, self.lb_facts = self.lb_find(0,arg_amodule.params['lb_name'],self.rg_id) return @@ -98,7 +115,8 @@ class decort_lb(DecortController): self.amodule.params['ext_net_id'], self.amodule.params['ha_lb'], self.amodule.params['annotation']) - if self.amodule.params['backends'] or self.amodule.params['frontends']: + if self.lb_id and (self.amodule.params['backends'] or + self.amodule.params['frontends']): self.lb_id, self.lb_facts = self.lb_find(0,self.amodule.params['lb_name'],self.rg_id) self.lb_update( self.lb_facts['primaryNode'], @@ -114,10 +132,10 @@ class decort_lb(DecortController): def action(self,d_state='',restore=False): if restore == True: - self.lb_restore(arg_vins_id=self.lb_id) - self.lb_state(self.vins_facts, 'enabled') - self.lb_facts['status'] = "ENABLED" - self.lb_facts['techStatus'] = "STARTED" + self.lb_restore(lb_id=self.lb_id) + _, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id) + self.lb_state(self.lb_facts, 'enabled') + _, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id) self.lb_update( self.lb_facts['primaryNode'], @@ -132,6 +150,14 @@ class decort_lb(DecortController): if d_state != '': self.lb_state(self.lb_facts, d_state) + _, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id) + + if (d_state == 'enabled' and + self.lb_facts.get('status') == 'ENABLED' and + self.lb_facts.get('techStatus') == 'STOPPED'): + self.lb_state(self.lb_facts, 'started') + _, self.lb_facts = self._lb_get_by_id(lb_id=self.lb_id) + return def delete(self): @@ -148,7 +174,7 @@ class decort_lb(DecortController): self.result['changed'] = False if self.lb_id: self.result['msg'] = ("No state change required for LB ID {} because of its " - "current status '{}'.").format(self.lb_id, self.vins_facts['status']) + "current status '{}'.").format(self.lb_id, self.lb_facts['status']) else: self.result['msg'] = ("No state change to '{}' can be done for " "non-existent LB instance.").format(self.amodule.params['state']) @@ -187,7 +213,7 @@ class decort_lb(DecortController): # in check mode return immediately with the default values return ret_dict - if self.vins_facts is None: + if self.lb_facts is None: # if void facts provided - change state value to ABSENT and return ret_dict['state'] = "ABSENT" return ret_dict @@ -288,23 +314,24 @@ def main(): elif decon.lb_facts['status'] == "DISABLED": if amodule.params['state'] == 'absent': decon.delete() - elif amodule.params['state'] in ('present', 'disabled'): + elif amodule.params['state'] == 'disabled': decon.action() - elif amodule.params['state'] == 'enabled': + elif amodule.params['state'] in ('enabled', 'present'): decon.action('enabled') elif decon.lb_facts['status'] in ["CREATED", "ENABLED"]: if amodule.params['state'] == 'absent': decon.delete() elif amodule.params['state'] in ('present', 'enabled'): - decon.action() + decon.action(d_state='enabled') elif amodule.params['state'] == 'disabled': decon.action('disabled') elif amodule.params['state'] in ('stopped', 'started','restart'): decon.action(amodule.params['state']) elif decon.lb_facts['status'] == "DELETED": if amodule.params['state'] in ['present', 'enabled']: - decon.action(restore=True) - elif amodule.params['state'] == 'absent': + decon.action(d_state='enabled', restore=True) + elif (amodule.params['state'] == 'absent' and + amodule.params['permanently']): decon.delete() elif amodule.params['state'] == 'disabled': decon.error() diff --git a/library/decort_osimage.py b/library/decort_osimage.py index b78c89e..cbe84e5 100644 --- a/library/decort_osimage.py +++ b/library/decort_osimage.py @@ -313,9 +313,17 @@ class decort_osimage(DecortController): amodule.fail_json(**self.result) - if amodule.params['image_id'] != 0 and amodule.params['image_name']: - self.validated_image_id = amodule.params['image_id'] - if amodule.params['image_name']: + if amodule.params['virt_id'] != 0 and amodule.params['virt_name']: + self.validated_virt_image_id, image_facts =\ + self.decort_virt_image_find(amodule) + if (self.validated_virt_image_id and + amodule.params['virt_name'] != image_facts['name']): + self.decort_virt_image_rename(amodule) + self.result['msg'] = 'Virtual image renamed successfully' + elif amodule.params['image_id'] != 0 and amodule.params['image_name']: + self.validated_image_id, image_facts = self.decort_image_find(amodule) + if (self.validated_image_id and + amodule.params['image_name'] != image_facts['name']): decort_osimage.decort_image_rename(self,amodule) self.result['msg'] = ("Image renamed successfully") @@ -350,7 +358,7 @@ class decort_osimage(DecortController): hotresize=amodule.params['hotresize'], username=amodule.params['image_username'], password=amodule.params['image_password'], - account_Id=amodule.params['account_Id'], + account_Id=self.validated_account_id, usernameDL=amodule.params['usernameDL'], passwordDL=amodule.params['passwordDL'], sepId=amodule.params['sepId'], @@ -389,6 +397,12 @@ class decort_osimage(DecortController): image_id, image_facts = decort_osimage.decort_image_find(self, amodule) return image_id, image_facts + def decort_virt_image_rename(self, amodule): + image_facts = self.image_rename(imageId=self.validated_virt_image_id, + name=amodule.params['virt_name']) + self.result['msg'] = ("Virtual image renamed successfully") + 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): """Package a dictionary of OS image according to the decort_osimage module specification. This @@ -507,38 +521,15 @@ def main(): decon = decort_osimage(amodule) - if amodule.params['image_name'] or amodule.params['image_id']: - image_id, image_facts = decort_osimage.decort_image_find(decon, amodule) - decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id'] - 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) - - if amodule.params['state'] == "present" and decon.validated_image_id == 0 and amodule.params['image_name'] and amodule.params['url']: - decort_osimage.decort_image_create(decon,amodule) - decon.result['changed'] = True - image_id, image_facts = decort_osimage.decort_image_find(decon, amodule) - decon.result['msg'] = ("OS image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id']) - decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode) - decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id'] - - - elif amodule.params['state'] == "absent": - 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) - - - 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 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: 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']) @@ -547,19 +538,36 @@ def main(): decon.result['msg'] = ("Cannot find OS image") amodule.fail_json(**decon.result) - - if decon.validated_image_id: + if decon.validated_virt_image_id: if decort_osimage.decort_osimage_package_facts(image_facts)['linkto'] != decon.validated_image_id: decort_osimage.decort_virt_image_link(decon,amodule) decon.result['changed'] = True amodule.exit_json(**decon.result) - if decon.validated_virt_image_id > 0 and amodule.params['state'] == "absent": decon.result['msg'] = ("Osimage module cannot delete virtual images.") decon.result['failed'] = True amodule.exit_json(**decon.result) + elif amodule.params['image_name'] or amodule.params['image_id']: + image_id, image_facts = decort_osimage.decort_image_find(decon, amodule) + decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id'] + 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) + + if amodule.params['state'] == "present" and decon.validated_image_id == 0 and amodule.params['image_name'] and amodule.params['url']: + decort_osimage.decort_image_create(decon,amodule) + decon.result['changed'] = True + image_id, image_facts = decort_osimage.decort_image_find(decon, amodule) + decon.result['msg'] = ("OS image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id']) + decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode) + 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) if decon.result['failed'] == True: # we failed to find the specified image - fail the module diff --git a/library/decort_rg.py b/library/decort_rg.py index 78260e6..8f1f5a8 100644 --- a/library/decort_rg.py +++ b/library/decort_rg.py @@ -324,11 +324,14 @@ class decort_rg(DecortController): "", # this is location code. TODO: add module argument ) - self.validated_rg_id, self.rg_facts = self.rg_find(self.validated_acc_id, - self.validated_rg_id, - arg_rg_name="", - arg_check_state=False) - self.rg_should_exist = True + if 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.validated_rg_id, + arg_rg_name="", + arg_check_state=False + ) + self.rg_should_exist = True return def enable(self): @@ -493,8 +496,12 @@ def main(): elif decon.rg_facts['status'] == "DELETED": if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True: decon.destroy() - elif amodule.params['state'] == 'present': + elif (amodule.params['state'] == 'present' + or amodule.params['state'] == 'disabled'): + decon.restore() + elif amodule.params['state'] == 'enabled': decon.restore() + decon.enable() elif decon.rg_facts['status'] in ("DISABLED"): if amodule.params['state'] == 'absent': decon.destroy() @@ -503,9 +510,16 @@ def main(): else: if amodule.params['state'] in ('present', 'enabled'): - decon.create() - if amodule.params['access']: - decon.access() + if not amodule.params['rg_name']: + decon.result['failed'] = True + decon.result['msg'] = ( + 'Resource group could not be created because' + ' the "rg_name" parameter was not specified.' + ) + else: + decon.create() + if amodule.params['access'] and not amodule.check_mode: + decon.access() elif amodule.params['state'] in ('disabled'): decon.error() diff --git a/module_utils/decort_utils.py b/module_utils/decort_utils.py index 70c2765..b3cd0be 100644 --- a/module_utils/decort_utils.py +++ b/module_utils/decort_utils.py @@ -392,10 +392,12 @@ class DecortController(object): retry_counter = retry_counter - 1 else: self.result['failed'] = True - self.result['msg'] = ("Error when calling DECORT API '{}', HTTP status code '{}', " - "reason '{}', parameters '{}'.").format(api_resp.url, - api_resp.status_code, - api_resp.reason, arg_params) + self.result['msg'] = ( + f'Error when calling DECORT API {api_resp.url}' + f', HTTP status code {api_resp.status_code}' + f', reason "{api_resp.reason}"' + f', parameters {arg_params}, text {api_resp.text}.' + ) self.amodule.fail_json(**self.result) return None # actually, this directive will never be executed as fail_json aborts the script @@ -1102,10 +1104,6 @@ class DecortController(object): elif not new_ram: new_ram = comp_dict['ram'] - # stupid hack? - if new_ram > 1 and new_ram < 512: - new_ram = new_ram * 1024 - if comp_dict['cpus'] == new_cpu and comp_dict['ram'] == new_ram: # no need to call API in this case, as requested size is not different from the current one self.result['failed'] = False @@ -1247,7 +1245,7 @@ class DecortController(object): if comp_dict['affinityRules']: for rule in comp_dict['affinityRules']: del rule['guid'] - if rule not in aff: + if not aff or rule not in aff: affrule_del.append(rule) if aff: @@ -1259,7 +1257,7 @@ class DecortController(object): if comp_dict['antiAffinityRules']: for rule in comp_dict['antiAffinityRules']: del rule['guid'] - if rule not in aaff: + if not aaff or rule not in aaff: aaffrule_del.append(rule) if aaff: @@ -1388,7 +1386,6 @@ class DecortController(object): return image_record['id'], image_record self.result['failed'] = False - self.result['failed'] = True self.result['msg'] = ("Failed to find OS image by name '{}', SEP ID {}, pool '{}' for " "account ID '{}'.").format(image_name, sepid, pool, @@ -1441,7 +1438,7 @@ class DecortController(object): api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/list", api_params) # On success the above call will return here. On error it will abort execution by calling fail_json. images_list = json.loads(api_resp.content.decode('utf8')) - for image_record in images_list: + for image_record in images_list['data']: if image_record['name'] == virt_name and image_record['status'] == "CREATED" and image_record['type'] == "virtual": if sepid == 0 and pool == "": # if no filtering by SEP ID or pool name is requested, return the first match @@ -1450,7 +1447,6 @@ class DecortController(object): if full_match: return image_record['id'], image_record - self.result['failed'] = True self.result['msg'] = ("Failed to find virtual OS image by name '{}', SEP ID {}, pool '{}' for " "account ID '{}'.").format(virt_name, sepid, pool, @@ -1563,9 +1559,10 @@ class DecortController(object): @param (int) )rg_id: ID of the RG to find and return facts for. - @return: RG ID and a dictionary of RG facts as provided by rg/get API call. Note that if it fails - to find the RG with the specified ID, it may return 0 for ID and empty dictionary for the facts. So - it is suggested to check the return values accordingly. + @return: RG ID and a dictionary of RG facts as provided by rg/get + API call. Note that if it fails to find the RG with the specified ID, + it may return 0 for ID and empty dictionary for the facts. So it is + suggested to check the return values accordingly. """ ret_rg_id = 0 ret_rg_dict = dict() @@ -1575,14 +1572,41 @@ class DecortController(object): self.result['msg'] = "rg_get_by_id(): zero RG ID specified." self.amodule.fail_json(**self.result) - api_params = dict(rgId=rg_id, ) - api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/get", api_params) - if api_resp.status_code == 200: - ret_rg_id = rg_id - ret_rg_dict = json.loads(api_resp.content.decode('utf8')) + api_params = {'rgId': rg_id} + + # Get RG base info + api_rg_resp = self.decort_api_call( + arg_req_function=requests.post, + arg_api_name='/restmachine/cloudapi/rg/get', + arg_params=api_params + ) + if api_rg_resp.status_code != 200: + self.result['warning'] = ( + f'rg_get_by_id(): failed to get RG by ID {rg_id}.' + f' HTTP code {api_rg_resp.status_code}' + f', response {api_rg_resp.reason}.' + ) + return ret_rg_id, ret_rg_dict + ret_rg_id = rg_id + ret_rg_dict = api_rg_resp.json() + + # Get RG resources info + rg_status = ret_rg_dict.get('status') + if not rg_status or rg_status in ('DELETED', 'DESTROYED'): + return ret_rg_id, ret_rg_dict + api_rg_res_resp = self.decort_api_call( + arg_req_function=requests.post, + arg_api_name='/restmachine/cloudapi/rg/getResourceConsumption', + arg_params=api_params + ) + if api_rg_res_resp.status_code != 200: + self.result['warning'] = ( + f'rg_get_by_id(): failed to get RG Resources by ID {rg_id}.' + f' HTTP code {api_rg_res_resp.status_code}' + f', response {api_rg_res_resp.reason}.' + ) else: - self.result['warning'] = ("rg_get_by_id(): failed to get RG by ID {}. HTTP code {}, " - "response {}.").format(rg_id, api_resp.status_code, api_resp.reason) + ret_rg_dict['Resources'] = api_rg_res_resp.json() return ret_rg_id, ret_rg_dict @@ -1658,7 +1682,7 @@ class DecortController(object): self.amodule.fail_json(**self.result) # try to locate RG by name - start with getting all RGs IDs within the specified account #api_params['accountId'] = arg_account_id - api_params['includedeleted'] = False + api_params['includedeleted'] = True #api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/account/listRG", api_params) api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/list",api_params) if api_resp.status_code == 200: @@ -1841,7 +1865,7 @@ class DecortController(object): # - when quering resource limits, the keys are in the form of cloud units (CU_*) query_key_map = dict(cpu='CU_C', ram='CU_M', - disk='CU_D', + disk='CU_DM', ext_ips='CU_I', net_transfer='CU_NP',) set_key_map = dict(cpu='maxCPUCapacity', @@ -2250,9 +2274,9 @@ class DecortController(object): # self.result['msg'] = "vins_find(): cannot find Account ID {}.".format(account_id) # self.amodule.fail_json(**self.result) # NOTE: account's 'vins' attribute does not list destroyed ViNSes! - for runner in rg_facts['vins']: - # api_params['vinsId'] = runner - ret_vins_id, ret_vins_facts = self._vins_get_by_id(runner) + account_vinses = self._get_all_account_vinses(account_id) + for vins in account_vinses: + ret_vins_id, ret_vins_facts = self._vins_get_by_id(vins['id']) if ret_vins_id and ret_vins_facts['name'] == vins_name: if not check_state or ret_vins_facts['status'] not in VINS_INVALID_STATES: return ret_vins_id, ret_vins_facts @@ -2729,7 +2753,7 @@ class DecortController(object): "write_iops_sec_max", "size_iops_sec", ): - if val and val < self.MIN_IOPS: + if val and val < MIN_IOPS: self.result['msg'] = (f"{arg} was set below the minimum iops {MIN_IOPS}: {val} provided") return @@ -2812,12 +2836,6 @@ class DecortController(object): DISK_INVALID_STATES = ["MODELED", "CREATING", "DELETING", "DESTROYING"] - if self.amodule.check_mode: - self.result['failed'] = False - self.result['msg'] = "disk_find() in check mode: find Disk ID {} / name '{}' was requested.".format(disk_id, - name) - return - ret_disk_id = 0 ret_disk_facts = None @@ -2833,10 +2851,18 @@ class DecortController(object): return 0, None elif name: if account_id: - api_params = dict(accountId=account_id,name=name) + api_params = { + 'accountId': account_id, + 'name': name, + 'show_all': True + } api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/search", api_params) + disks_list = api_resp.json() + # Filtering disks by status + excluded_statuses = ('PURGED', 'DESTROYED') + filter_f = lambda x: x.get('status') not in excluded_statuses + disks_list = [d for d in disks_list if filter_f(d)] # the above call may return more than one matching disk - disks_list = json.loads(api_resp.content.decode('utf8')) if len(disks_list) == 0: return 0, None elif len(disks_list) > 1: @@ -3108,7 +3134,7 @@ class DecortController(object): if runner['vmId'] == comp_facts['id']: existing_rules.append(runner) - if not len(existing_rules) and not len(new_rules): + if not existing_rules and not new_rules: self.result['failed'] = False self.result['warning'] = ("pfw_configure(): both existing and new port forwarding rule lists " "for Compute ID {} are empty - nothing to do.").format(comp_facts['id']) @@ -3116,9 +3142,15 @@ class DecortController(object): if new_rules == None or len(new_rules) == 0: # delete all existing rules for this Compute - api_params = dict(vinsId=vins_facts['id'], - ruleId=-1) - self.decort_api_call(requests.post, "/restmachine/cloudapi/vins/natRuleDel", api_params) + for rule in existing_rules: + self.decort_api_call( + arg_req_function=requests.post, + arg_api_name="/restmachine/cloudapi/vins/natRuleDel", + arg_params={ + 'vinsId': vins_facts['id'], + 'ruleId': rule['id'] + } + ) self.result['changed'] = True return ret_rules @@ -3456,7 +3488,7 @@ class DecortController(object): api_params = dict(name=k8s_name, rgId=rg_id, k8ciId=k8ci_id, - vins_Id=vins_id, + vinsId=vins_id, workerGroupName=def_wg_name, networkPlugin=plugin, masterNum=master_count, @@ -3488,6 +3520,7 @@ class DecortController(object): extnetOnly=extnet_only, ) + upload_files = None if oidc_cert: upload_files = {'oidcCertificate': ('cert.pem', str(oidc_cert),'application/x-x509-ca-cert')} @@ -3534,9 +3567,15 @@ class DecortController(object): self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_workers_modify") + if self.amodule.check_mode: + result_msg = 'k8s_workers_modify() in check mode: No changing.' + if self.result.get('msg'): + self.result['msg'] += f'\n{result_msg}' + else: + self.result['msg'] = result_msg + return if self.k8s_info['techStatus'] != "STARTED": - self.result['changed'] = False self.result['msg'] = ("k8s_workers_modify(): Can't modify with TechStatus other then STARTED") return @@ -3698,6 +3737,14 @@ class DecortController(object): self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "bservice_provision") + if self.amodule.check_mode: + result_msg = 'bservice_provision() in check mode: No changing.' + if self.result.get('msg'): + self.result['msg'] += f'\n{result_msg}' + else: + self.result['msg'] = result_msg + return 0 + api_url = "/restmachine/cloudapi/bservice/create" api_params = dict( name = bs_name, @@ -3827,11 +3874,12 @@ class DecortController(object): self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "group_find") if group_id == 0: - try: - i = bs_info['groupsName'].index(group_name) - except: - return 0,None - group_id = int(bs_info['groups'][i]) + for group in bs_info['groups']: + if group['name'] == group_name: + return self._group_get_by_id(bs_id=bs_id, + g_id=group['id']) + return 0, None + return self._group_get_by_id(bs_id,group_id) def group_state(self,bs_id,gr_id,desired_state): @@ -3925,7 +3973,7 @@ class DecortController(object): else: list_extnet.append(net['id']) - if gr_dict['vinses'] != list_vins: + if sorted(gr_dict['vinses']) != sorted(list_vins): api_url = "/restmachine/cloudapi/bservice/groupUpdateVins" api_params = dict( serviceId=bs_id, @@ -3946,11 +3994,7 @@ class DecortController(object): ): self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "group_provision") - - list_vins= list() - for net in arg_network: - if net['type'] == 'VINS': - list_vins.append(net['id']) + api_url = "/restmachine/cloudapi/bservice/groupAdd" api_params = dict( serviceId = bs_id, @@ -3962,13 +4006,15 @@ class DecortController(object): imageId = arg_image_id, driver = arg_driver, role = arg_role, - vinses = list_vins, + vinses = [n['id'] for n in arg_network if n['type'] == 'VINS'], + extnets = [n['id'] for n in arg_network if n['type'] == 'EXTNET'], timeoutStart = arg_timeout ) - self.decort_api_call(requests.post, api_url, api_params) + api_resp = self.decort_api_call(requests.post, api_url, api_params) + new_bsgroup_id = int(api_resp.text) self.result['failed'] = False self.result['changed'] = True - return + return new_bsgroup_id def group_delete(self,bs_id,gr_id): @@ -4023,8 +4069,8 @@ class DecortController(object): self.result['msg'] = "_rg_listlb(): zero RG ID specified." self.amodule.fail_json(**self.result) - api_params = dict(rgId=rg_id) - api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/listLb", api_params) + api_params = dict(rgId=rg_id, includedeleted=True) + api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/lb/list", api_params) if api_resp.status_code == 200: ret_rg_vins_list = json.loads(api_resp.content.decode('utf8')) else: @@ -4038,7 +4084,7 @@ class DecortController(object): @returns: LB ID and dictionary with LB facts. """ - LB_INVALID_STATES = ["ENABLING", "DISABLING", "DELETING", "DELETED", "DESTROYING", "DESTROYED"] + LB_INVALID_STATES = ["ENABLING", "DISABLING", "DELETING", "DESTROYING", "DESTROYED"] ret_lb_id = 0 ret_lb_facts = None @@ -4105,7 +4151,7 @@ class DecortController(object): vinsId=vins_id, highlyAvailable=ha_status, start=start, - decs=annotation + desc=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. @@ -4144,7 +4190,7 @@ class DecortController(object): NOP_STATES_FOR_LB_CHANGE = ["MODELED", "DISABLING", "ENABLING", "DELETING", "DELETED", "DESTROYING", "DESTROYED"] - VALID_TARGET_STATES = ["enabled", "disabled","restart"] + VALID_TARGET_STATES = ["enabled", "disabled","restart", 'started'] VALID_TARGET_TSTATES = ["STARTED","STOPPED"] if lb_dict['status'] in NOP_STATES_FOR_LB_CHANGE: @@ -4435,6 +4481,15 @@ class DecortController(object): def lb_update(self,prime,front_ha_ip,back_ha_ip,lb_backends=[],lb_frontends=[],mod_backends=[],mod_servers=[],mod_frontends=[]): self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "lb_update") + + if self.amodule.check_mode: + result_msg = 'lb_update() in check mode: No changing.' + if self.result.get('msg'): + self.result['msg'] += f'\n{result_msg}' + else: + self.result['msg'] = result_msg + return + #lists from module and cloud mod_backs_list = [back['name'] for back in mod_backends] lb_backs_list = [back['name'] for back in lb_backends] @@ -4445,9 +4500,6 @@ class DecortController(object): #FE - mod_front_list = [front['name'] for front in mod_frontends] - lb_front_list = [front['name'] for front in lb_frontends] - if del_list_backs: self._lb_delete_backends( @@ -4470,6 +4522,9 @@ class DecortController(object): mod_servers ) + mod_front_list = [front['name'] for front in mod_frontends] + lb_front_list = [front['name'] for front in lb_frontends] + del_list_fronts = set(lb_front_list).difference(mod_front_list) add_list_fronts = set(mod_front_list).difference(lb_front_list) upd_front_list = set(lb_front_list).intersection(mod_front_list) @@ -4524,7 +4579,7 @@ class DecortController(object): api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/lb/frontendDelete", api_params) #del from cloud dict if type(front)==dict: - del self.lb_facts['frontends'][front['name']] + self.lb_facts['frontends'].remove(front) self.result['changed'] = True return @@ -4547,7 +4602,8 @@ class DecortController(object): bind['address']if "address" in bind else bind_ip, bind['port'], ) - return + + return def _lb_create_backends(self,back_list,mod_backs,mod_serv): ''' Create backends and add servers to them @@ -4630,8 +4686,8 @@ class DecortController(object): serverName = server['name'], address = server['address'], port = mod_back['port'], - check = server['check'] if "check" in server else None, - **server['server_settings'] if "server_settings" in server else {}, + check = mod_back['check'] if "check" in mod_back else None, + **mod_back['server_settings'] if "server_settings" in mod_back else {}, ) api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/lb/backendServerAdd", api_params) self.result['changed'] = True @@ -4690,11 +4746,14 @@ class DecortController(object): lb_bind, = list(filter(lambda i: i['name'] == bind['name'],lb_front['bindings'])) del lb_bind['guid'] + if not bind.get('address'): + bind['address'] = bind_ip + if dict(sorted(bind.items())) != dict(sorted(lb_bind.items())): self._lb_bind_frontend( front, bind['name'], - bind['address'] if "address" in bind else bind_ip, + bind['address'], bind['port'], update=True, )