#!/usr/bin/python DOCUMENTATION = r''' --- module: decort_rg description: See L(Module Documentation,https://repository.basistech.ru/BASIS/decort-ansible/wiki/Home). ''' from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback from ansible.module_utils.decort_utils import * class decort_rg(DecortController): def __init__(self): super(decort_rg, self).__init__(AnsibleModule(**self.amodule_init_args)) amodule = self.amodule self.validated_acc_id = 0 self.validated_rg_id = 0 self.validated_rg_facts = None 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.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 for rg_item in self.rg_facts['acl']: if rg_item['userGroupId'] == self.amodule.params['access']['user']: acc_granted = True if self.amodule.params['access']['action'] == 'grant': if rg_item['right'] != self.amodule.params['access']['right']: should_change_access = True if self.amodule.params['access']['action'] == 'revoke': should_change_access = True if acc_granted == False and self.amodule.params['access']['action'] == 'grant': should_change_access = True if should_change_access == True: self.rg_access(self.validated_rg_id, self.amodule.params['access']) self.rg_facts['access'] = self.amodule.params['access'] self.rg_should_exist = True return def error(self): self.result['failed'] = True self.result['changed'] = False if self.validated_rg_id > 0: self.result['msg'] = ("Invalid target state '{}' requested for rg ID {} in the " "current status '{}'.").format(self.validated_rg_id, self.amodule.params['state'], self.rg_facts['status']) else: self.result['msg'] = ("Invalid target state '{}' requested for non-existent rg name '{}' " "in account ID {} ").format(self.amodule.params['state'], self.amodule.params['rg_name'], self.validated_acc_id) return def update(self): resources = self.rg_facts['Resources']['Reserved'] incorrect_quota = dict(Requested=dict(), Reserved=dict(),) query_key_map = dict(cpu='cpu', ram='ram', disk='disksize', ext_ips='extips', net_transfer='exttraffic',) if self.amodule.params['quotas']: for quota_item in self.amodule.params['quotas']: if self.amodule.params['quotas'][quota_item] < resources[query_key_map[quota_item]]: incorrect_quota['Requested'][quota_item]=self.amodule.params['quotas'][quota_item] incorrect_quota['Reserved'][quota_item]=resources[query_key_map[quota_item]] if incorrect_quota['Requested']: self.result['msg'] = ("Cannot limit less than already reserved'{}'").format(incorrect_quota) self.result['failed'] = True 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): 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 def create(self): self.validated_rg_id = self.rg_provision(self.validated_acc_id, self.amodule.params['rg_name'], self.amodule.params['owner'], self.amodule.params['description'], self.amodule.params['resType'], self.amodule.params['def_netType'], self.amodule.params['ipcidr'], self.amodule.params['extNetId'], self.amodule.params['extNetIp'], self.amodule.params['quotas'], "", # this is location code. TODO: add module argument ) 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): self.rg_enable(self.validated_rg_id, self.amodule.params['state']) if self.amodule.params['state'] == "enabled": self.rg_facts['status'] = 'CREATED' else: self.rg_facts['status'] = 'DISABLED' self.rg_should_exist = True return def restore(self): self.rg_restore(self.validated_rg_id) self.rg_facts['status'] = 'DISABLED' self.rg_should_exist = True return def destroy(self): 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: self.rg_facts['status'] = 'DELETED' self.rg_should_exist = False return def package_facts(self, check_mode=False): """Package a dictionary of RG facts according to the decort_rg module specification. This dictionary will be returned to the upstream Ansible engine at the completion of the module run. @param arg_rg_facts: dictionary with RG facts as returned by API call to .../rg/get @param arg_check_mode: boolean that tells if this Ansible module is run in check mode """ ret_dict = dict(id=0, name="none", state="CHECK_MODE", ) if check_mode: # in check mode return immediately with the default values return ret_dict #if arg_rg_facts is None: # # if void facts provided - change state value to ABSENT and return # ret_dict['state'] = "ABSENT" # return ret_dict ret_dict['id'] = self.rg_facts['id'] ret_dict['name'] = self.rg_facts['name'] ret_dict['state'] = self.rg_facts['status'] ret_dict['account_id'] = self.rg_facts['accountId'] ret_dict['gid'] = self.rg_facts['gid'] ret_dict['quota'] = self.rg_facts['resourceLimits'] ret_dict['resTypes'] = self.rg_facts['resourceTypes'] ret_dict['defNetId'] = self.rg_facts['def_net_id'] 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 @property def amodule_init_args(self) -> dict: return self.pack_amodule_init_args( argument_spec=dict( account_id=dict( type='int', ), account_name=dict( type='str', default='', ), access=dict( type='dict', ), description=dict( type='str', default='', ), def_netType=dict( type='str', choices=[ 'PRIVATE', 'PUBLIC', 'NONE', ], default='PRIVATE', ), def_netId=dict( type='int', ), extNetId=dict( type='int', default=0, ), extNetIp=dict( type='str', default='', ), owner=dict( type='str', default='', ), ipcidr=dict( type='str', default='', ), rename=dict( type='str', default='', ), quotas=dict( type='dict', ), resType=dict( type='list', ), state=dict( type='str', default='present', choices=[ 'absent', 'disabled', 'enabled', 'present', ], ), permanently=dict( type='bool', default='False', ), rg_name=dict( type='str', ), rg_id=dict( type='int', ), 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, ) ), supports_check_mode=True, ) # Workflow digest: # 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECORTController # 2) check if the RG with the specified id or rg_name:name exists # 3) if RG does not exist -> deploy # 4) if RG exists: check desired state, desired configuration -> initiate action accordingly # 5) report result to Ansible def main(): decon = decort_rg() amodule = decon.amodule #amodule.check_mode=True if decon.validated_rg_id > 0: if decon.rg_facts['status'] in ["MODELED", "DISABLING", "ENABLING", "DELETING", "DESTROYING", "CONFIRMED"]: decon.error() elif decon.rg_facts['status'] in ("CREATED"): if amodule.params['state'] == 'absent': decon.destroy() 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'] != "" or amodule.params['sep_pools'] is not None ): decon.update() if amodule.params['access']: decon.access() if amodule.params['def_netType'] is not None: decon.setDefNet() elif decon.rg_facts['status'] == "DELETED": if amodule.params['state'] == 'absent' and amodule.params['permanently'] == True: decon.destroy() 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() elif amodule.params['state'] == ("enabled"): decon.enable() else: if amodule.params['state'] in ('present', 'enabled'): 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() if decon.result['failed']: 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: amodule.exit_json(**decon.result) if __name__ == "__main__": main()