def main(): argument_specs = dict( old_password=dict(type='str', required=True, no_log=True), # Flag to specify priority of old/new password while establishing session with controller. # To handle both Saas and conventional (Entire state in playbook) scenario. force_change=dict(type='bool', default=False)) argument_specs.update(avi_common_argument_spec()) module = AnsibleModule(argument_spec=argument_specs) if not HAS_AVI: return module.fail_json(msg=( 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' 'For more details visit https://github.com/avinetworks/sdk.')) api_creds = AviCredentials() api_creds.update_from_ansible_module(module) old_password = module.params.get('old_password') force_change = module.params.get('force_change', False) data = {'old_password': old_password, 'password': api_creds.password} # First try old password if 'force_change' is set to true if force_change: first_pwd = old_password second_pwd = api_creds.password # First try new password if 'force_change' is set to false or not specified in playbook. else: first_pwd = api_creds.password second_pwd = old_password password_changed = False try: api = ApiSession.get_session(api_creds.controller, api_creds.username, password=first_pwd, timeout=api_creds.timeout, tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, port=api_creds.port) if force_change: rsp = api.put('useraccount', data=data) if rsp: password_changed = True except Exception: pass if not password_changed: api = ApiSession.get_session(api_creds.controller, api_creds.username, password=second_pwd, timeout=api_creds.timeout, tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, port=api_creds.port) if not force_change: rsp = api.put('useraccount', data=data) if rsp: password_changed = True if password_changed: return ansible_return(module, rsp, True, req=data) else: return ansible_return(module, rsp, False, req=data)
def main(): argument_specs = dict() argument_specs.update(avi_common_argument_spec()) module = AnsibleModule(argument_spec=argument_specs) if not HAS_AVI: return module.fail_json(msg=( 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' 'For more details visit https://github.com/avinetworks/sdk.')) try: api_creds = AviCredentials() api_creds.update_from_ansible_module(module) api = ApiSession.get_session(api_creds.controller, api_creds.username, password=api_creds.password, timeout=api_creds.timeout, tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, port=api_creds.port) remote_api_version = api.remote_api_version remote = {} for key in remote_api_version.keys(): remote[key.lower()] = remote_api_version[key] api.close() module.exit_json(changed=False, obj=remote) except Exception as e: module.fail_json(msg=("Unable to get an AVI session. %s" % e))
def run(self, terms, variables=None, avi_credentials=None, **kwargs): api_creds = AviCredentials(**avi_credentials) # Create the session using avi_credentials try: avi = ApiSession(avi_credentials=api_creds) except Exception as e: raise AnsibleError(to_native(e)) # Return an empty list if the object is not found rsp = [] try: path = kwargs.pop('obj_type') except KeyError: raise AnsibleError("Please pass the obj_type for lookup") if kwargs.get('obj_name', None): name = kwargs.pop('obj_name') try: display.v("Fetching obj: %s of type: %s" % (name, path)) rsp_data = avi.get_object_by_name(path, name, **kwargs) if rsp_data: # Append the return data only if it is not None. i.e object # with specified name is present rsp.append(rsp_data) except AviServerError as e: raise AnsibleError(to_native(e)) elif kwargs.get('obj_uuid', None): obj_uuid = kwargs.pop('obj_uuid') obj_path = "%s/%s" % (path, obj_uuid) display.v("Fetching obj: %s of type: %s" % (obj_uuid, path)) rsp = _api(avi, obj_path, **kwargs) else: display.v("Fetching all objects of type: %s" % path) rsp = _api(avi, path, **kwargs) return rsp
def main(): argument_specs = dict(http_method=dict( required=True, choices=['get', 'put', 'post', 'patch', 'delete']), path=dict(type='str', required=True), params=dict(type='dict'), data=dict(type='jsonarg'), timeout=dict(type='int', default=60)) argument_specs.update(avi_common_argument_spec()) module = AnsibleModule(argument_spec=argument_specs) if not HAS_AVI: return module.fail_json(msg=( 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' 'For more details visit https://github.com/avinetworks/sdk.')) api_creds = AviCredentials() api_creds.update_from_ansible_module(module) api = ApiSession.get_session(api_creds.controller, api_creds.username, password=api_creds.password, timeout=api_creds.timeout, tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, port=api_creds.port) tenant_uuid = api_creds.tenant_uuid tenant = api_creds.tenant timeout = int(module.params.get('timeout')) # path is a required argument path = module.params.get('path', '') params = module.params.get('params', None) data = module.params.get('data', None) # Get the api_version from module. api_version = api_creds.api_version if data is not None: data = json.loads(data) method = module.params['http_method'] existing_obj = None changed = method != 'get' gparams = deepcopy(params) if params else {} gparams.update({'include_refs': '', 'include_name': ''}) # API methods not allowed api_get_not_allowed = ["cluster", "gslbsiteops"] api_post_not_allowed = ["alert", "fileservice"] api_put_not_allowed = ["backup"] if method == 'post' and not any( path.startswith(uri) for uri in api_post_not_allowed): # TODO: Above condition should be updated after AV-38981 is fixed # need to check if object already exists. In that case # change the method to be put try: using_collection = False if not any(path.startswith(uri) for uri in api_get_not_allowed): if 'name' in data: gparams['name'] = data['name'] using_collection = True if not any(path.startswith(uri) for uri in api_get_not_allowed): rsp = api.get(path, tenant=tenant, tenant_uuid=tenant_uuid, params=gparams, api_version=api_version) existing_obj = rsp.json() if using_collection: existing_obj = existing_obj['results'][0] except (IndexError, KeyError): # object is not found pass else: if not any(path.startswith(uri) for uri in api_get_not_allowed): # object is present method = 'put' path += '/' + existing_obj['uuid'] if method == 'put' and not any( path.startswith(uri) for uri in api_put_not_allowed): # put can happen with when full path is specified or it is put + post if existing_obj is None: using_collection = False if ((len(path.split('/')) == 1) and ('name' in data) and (not any(path.startswith(uri) for uri in api_get_not_allowed))): gparams['name'] = data['name'] using_collection = True rsp = api.get(path, tenant=tenant, tenant_uuid=tenant_uuid, params=gparams, api_version=api_version) rsp_data = rsp.json() if using_collection: if rsp_data['results']: existing_obj = rsp_data['results'][0] path += '/' + existing_obj['uuid'] else: method = 'post' else: if rsp.status_code == 404: method = 'post' else: existing_obj = rsp_data if existing_obj: changed = not avi_obj_cmp(data, existing_obj) cleanup_absent_fields(data) if method == 'patch': rsp = api.get(path, tenant=tenant, tenant_uuid=tenant_uuid, params=gparams, api_version=api_version) existing_obj = rsp.json() if (method == 'put' and changed) or (method != 'put'): fn = getattr(api, method) rsp = fn(path, tenant=tenant, tenant_uuid=tenant, timeout=timeout, params=params, data=data, api_version=api_version) else: rsp = None if method == 'delete' and rsp.status_code == 404: changed = False rsp.status_code = 200 if method == 'patch' and existing_obj and rsp.status_code < 299: # Ideally the comparison should happen with the return values # from the patch API call. However, currently Avi API are # returning different hostname when GET is used vs Patch. # tracked as AV-12561 if path.startswith('pool'): time.sleep(1) gparams = deepcopy(params) if params else {} gparams.update({'include_refs': '', 'include_name': ''}) rsp = api.get(path, tenant=tenant, tenant_uuid=tenant_uuid, params=gparams, api_version=api_version) new_obj = rsp.json() changed = not avi_obj_cmp(new_obj, existing_obj) if rsp is None: return module.exit_json(changed=changed, obj=existing_obj) return ansible_return(module, rsp, changed, req=data)
def main(): argument_specs = dict(params=dict(type='dict'), data=dict(type='dict'), name=dict(type='str', required=True), state=dict(default='present', choices=['absent', 'present'])) argument_specs.update(avi_common_argument_spec()) module = AnsibleModule(argument_spec=argument_specs) if not HAS_AVI: return module.fail_json(msg=( 'Avi python API SDK (avisdk>=17.1) or ansible>=2.8 is not installed. ' 'For more details visit https://github.com/avinetworks/sdk.')) api_creds = AviCredentials() api_creds.update_from_ansible_module(module) api = ApiSession.get_session(api_creds.controller, api_creds.username, password=api_creds.password, timeout=api_creds.timeout, tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, port=api_creds.port) tenant = api_creds.tenant tenant_uuid = api_creds.tenant_uuid params = module.params.get('params', None) data = module.params.get('data', None) gparams = deepcopy(params) if params else {} gparams.update({'include_refs': '', 'include_name': ''}) name = module.params.get('name', '') state = module.params['state'] # Get the api version from module. api_version = api_creds.api_version """ state: present 1. Check if the GSLB service is present 2. If not then create the GSLB service with the member 3. Check if the group exists 4. if not then create the group with the member 5. Check if the member is present if not then add the member state: absent 1. check if GSLB service is present if not then exit 2. check if group is present. if not then exit 3. check if member is present. if present then remove it. """ obj_type = 'gslbservice' # Added api version to call existing_obj = api.get_object_by_name(obj_type, name, tenant=tenant, tenant_uuid=tenant_uuid, params={ 'include_refs': '', 'include_name': '' }, api_version=api_version) check_mode = module.check_mode if state == 'absent': # Added api version to call changed, rsp = delete_member(module, check_mode, api, tenant, tenant_uuid, existing_obj, data, api_version) else: # Added api version to call changed, rsp = add_member(module, check_mode, api, tenant, tenant_uuid, existing_obj, data, name, api_version) if check_mode or not changed: return module.exit_json(changed=changed, obj=existing_obj) return ansible_return(module, rsp, changed, req=data)
def main(): argument_specs = dict( state=dict(default='present', choices=['absent', 'present']), avi_api_update_method=dict(default='put', choices=['put', 'patch']), avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), async_interval=dict(type='int', ), clear_on_max_retries=dict(type='int', ), client_ip_addr_group=dict(type='dict', ), description=dict(type='str', ), dns_configs=dict(type='list', ), is_federated=dict(type='bool', ), leader_cluster_uuid=dict(type='str', required=True), maintenance_mode=dict(type='bool', ), name=dict(type='str', required=True), send_interval=dict(type='int', ), send_interval_prior_to_maintenance_mode=dict(type='int', ), sites=dict(type='list', ), tenant_ref=dict(type='str', ), third_party_sites=dict(type='list', ), url=dict(type='str', ), uuid=dict(type='str', ), view_id=dict(type='int', ), ) argument_specs.update(avi_common_argument_spec()) module = AnsibleModule(argument_spec=argument_specs, supports_check_mode=True) if not HAS_AVI: return module.fail_json(msg=( 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' 'For more details visit https://github.com/avinetworks/sdk.')) api_method = module.params['avi_api_update_method'] if str(api_method).lower() == 'patch': patch_op = module.params['avi_api_patch_op'] # Create controller session api_creds = AviCredentials() api_creds.update_from_ansible_module(module) api = ApiSession.get_session(api_creds.controller, api_creds.username, password=api_creds.password, timeout=api_creds.timeout, tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, port=api_creds.port) # Get existing gslb objects rsp = api.get('gslb', api_version=api_creds.api_version) existing_gslb = rsp.json() gslb = existing_gslb['results'] sites = module.params['gslb_sites_config'] for gslb_obj in gslb: # Update/Delete domain names in dns_configs fields in gslb object. if 'dns_configs' in module.params: if gslb_obj['leader_cluster_uuid'] == module.params[ 'leader_cluster_uuid']: if str(patch_op).lower() == 'delete': gslb_obj['dns_configs'] = [] elif str(patch_op).lower() == 'add': if module.params['dns_configs'] not in gslb_obj[ 'dns_configs']: gslb_obj['dns_configs'].extend( module.params['dns_configs']) else: gslb_obj['dns_configs'] = module.params['dns_configs'] # Update/Delete sites configuration if sites: for site_obj in gslb_obj['sites']: dns_vses = site_obj.get('dns_vses', []) for obj in sites: config_for = obj.get('ip_addr', None) if not config_for: return module.fail_json(msg=( "ip_addr of site in a configuration is mandatory. " "Please provide ip_addr i.e. gslb site's ip.")) if config_for == site_obj['ip_addresses'][0]['addr']: if str(patch_op).lower() == 'delete': site_obj['dns_vses'] = [] else: # Modify existing gslb sites object for key, val in obj.items(): if key == 'dns_vses' and str( patch_op).lower() == 'add': found = False # Check dns_vses field already exists on the controller for v in dns_vses: if val[0]['dns_vs_uuid'] != v[ 'dns_vs_uuid']: found = True break if not found: dns_vses.extend(val) else: site_obj[key] = val if str(patch_op).lower() == 'add': site_obj['dns_vses'] = dns_vses uni_dns_configs = [ dict(tupleized) for tupleized in set( tuple(item.items()) for item in gslb_obj['dns_configs']) ] gslb_obj['dns_configs'] = uni_dns_configs module.params.update(gslb_obj) module.params.update({ 'avi_api_update_method': 'put', 'state': 'present' }) return avi_ansible_api(module, 'gslb', set([]))
def avi_ansible_api(module, obj_type, sensitive_fields): """ This converts the Ansible module into AVI object and invokes APIs :param module: Ansible module :param obj_type: string representing Avi object type :param sensitive_fields: sensitive fields to be excluded for comparison purposes. Returns: success: module.exit_json with obj=avi object faliure: module.fail_json """ api_creds = AviCredentials() api_creds.update_from_ansible_module(module) api_context = get_api_context(module, api_creds) if api_context: api = ApiSession.get_session(api_creds.controller, api_creds.username, password=api_creds.password, timeout=api_creds.timeout, tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, token=api_context['csrftoken'], port=api_creds.port, session_id=api_context['session_id'], csrftoken=api_context['csrftoken']) else: api = ApiSession.get_session(api_creds.controller, api_creds.username, password=api_creds.password, timeout=api_creds.timeout, tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, port=api_creds.port) state = module.params['state'] # Get the api version. avi_update_method = module.params.get('avi_api_update_method', 'put') avi_patch_op = module.params.get('avi_api_patch_op', 'add') api_version = api_creds.api_version name = module.params.get('name', None) # Added Support to get uuid uuid = module.params.get('uuid', None) check_mode = module.check_mode if uuid and obj_type != 'cluster': obj_path = '%s/%s' % (obj_type, uuid) else: obj_path = '%s/' % obj_type obj = deepcopy(module.params) tenant = obj.pop('tenant', '') tenant_uuid = obj.pop('tenant_uuid', '') # obj.pop('cloud_ref', None) for k in POP_FIELDS: obj.pop(k, None) purge_optional_fields(obj, module) # Special code to handle situation where object has a field # named username. This is used in case of api/user # The following code copies the username and password # from the obj_username and obj_password fields. if 'obj_username' in obj: obj['username'] = obj['obj_username'] obj.pop('obj_username') if 'obj_password' in obj: obj['password'] = obj['obj_password'] obj.pop('obj_password') if 'full_name' not in obj and 'name' in obj and obj_type == "user": obj['full_name'] = obj['name'] # Special case as name represent full_name in user module # As per API response, name is always same as username regardless of full_name obj['name'] = obj['username'] log.info('passed object %s ', obj) if uuid: # Get the object based on uuid. try: existing_obj = api.get(obj_path, tenant=tenant, tenant_uuid=tenant_uuid, params={ 'include_refs': '', 'include_name': '' }, api_version=api_version) existing_obj = existing_obj.json() except ObjectNotFound: existing_obj = None elif name: params = {'include_refs': '', 'include_name': ''} if obj.get('cloud_ref', None): # this is the case when gets have to be scoped with cloud cloud = obj['cloud_ref'].split('name=')[1] params['cloud_ref.name'] = cloud existing_obj = api.get_object_by_name(obj_type, name, tenant=tenant, tenant_uuid=tenant_uuid, params=params, api_version=api_version) # Need to check if tenant_ref was provided and the object returned # is actually in admin tenant. if existing_obj and 'tenant_ref' in obj and 'tenant_ref' in existing_obj: # https://10.10.25.42/api/tenant/admin#admin existing_obj_tenant = existing_obj['tenant_ref'].split('#')[1] obj_tenant = obj['tenant_ref'].split('name=')[1] if obj_tenant != existing_obj_tenant: existing_obj = None else: # added api version to avi api call. existing_obj = api.get(obj_path, tenant=tenant, tenant_uuid=tenant_uuid, params={ 'include_refs': '', 'include_name': '' }, api_version=api_version).json() if state == 'absent': rsp = None changed = False err = False if not check_mode and existing_obj: try: if name is not None: # added api version to avi api call. rsp = api.delete_by_name(obj_type, name, tenant=tenant, tenant_uuid=tenant_uuid, api_version=api_version) else: # added api version to avi api call. rsp = api.delete(obj_path, tenant=tenant, tenant_uuid=tenant_uuid, api_version=api_version) except ObjectNotFound: pass if check_mode and existing_obj: changed = True if rsp: if rsp.status_code == 204: changed = True else: err = True if not err: return ansible_return(module, rsp, changed, existing_obj=existing_obj, api_context=api.get_context()) elif rsp: return module.fail_json(msg=rsp.text) rsp = None req = None if existing_obj: # this is case of modify as object exists. should find out # if changed is true or not if name is not None and obj_type != 'cluster': obj_uuid = existing_obj['uuid'] obj_path = '%s/%s' % (obj_type, obj_uuid) if avi_update_method == 'put': changed = not avi_obj_cmp(obj, existing_obj, sensitive_fields) obj = cleanup_absent_fields(obj) if changed: req = obj if check_mode: # No need to process any further. rsp = AviCheckModeResponse(obj=existing_obj) else: rsp = api.put(obj_path, data=req, tenant=tenant, tenant_uuid=tenant_uuid, api_version=api_version) elif check_mode: rsp = AviCheckModeResponse(obj=existing_obj) else: if check_mode: # No need to process any further. rsp = AviCheckModeResponse(obj=existing_obj) changed = True else: obj.pop('name', None) patch_data = {avi_patch_op: obj} rsp = api.patch(obj_path, data=patch_data, tenant=tenant, tenant_uuid=tenant_uuid, api_version=api_version) obj = rsp.json() changed = not avi_obj_cmp(obj, existing_obj) if changed: log.debug('EXISTING OBJ %s', existing_obj) log.debug('NEW OBJ %s', obj) else: changed = True req = obj if check_mode: rsp = AviCheckModeResponse(obj=None) else: rsp = api.post(obj_type, data=obj, tenant=tenant, tenant_uuid=tenant_uuid, api_version=api_version) return ansible_return(module, rsp, changed, req, existing_obj=existing_obj, api_context=api.get_context())