mirror of https://github.com/ansible/ansible.git
Updated utils to remove Avi SDK dependency and Avi 18.2.2 version update (#54894)
* Updated utils to remove Avi SDK dependency and Avi 18.2.2 version update * Fixed the python 3.x errors failing for avi_disable_session_cache_as_fact not properly documented * Updated version added fields for new parameters * fixed pep8 errors * made requests import optional * removed setting requests to None * Added try catch for the avi helper methods such that any import fails then module fail gracefully. This was needed to pass the requests library not found error * removed deprecated modules. Also, trying another fix to deal with requests import error * Fixed python3 errors * fixed pep8, no-dict-iteritems and import test failures * added version 2.8 for new field * some more code cleanup and formatting * updated the fail message and fixed plint errors * added workaround for unicode pylint * fixed the version added for new parameter app_learning_memory_percent and removed unicode_literals import * Removed check of HAS_AVI for common argument spec * Updated version added value from 2.8 to 2.9 * Version added value fixes of CI errorpull/56596/head
parent
63e33f7e71
commit
b5935486da
@ -0,0 +1,556 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
"""
|
||||
Created on Aug 16, 2016
|
||||
|
||||
@author: Gaurav Rastogi (grastogi@avinetworks.com)
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
import sys
|
||||
from copy import deepcopy
|
||||
|
||||
try:
|
||||
from ansible.module_utils.network.avi.avi_api import (
|
||||
ApiSession, ObjectNotFound, avi_sdk_syslog_logger, AviCredentials, HAS_AVI)
|
||||
except ImportError:
|
||||
HAS_AVI = False
|
||||
|
||||
|
||||
if os.environ.get('AVI_LOG_HANDLER', '') != 'syslog':
|
||||
log = logging.getLogger(__name__)
|
||||
else:
|
||||
# Ansible does not allow logging from the modules.
|
||||
log = avi_sdk_syslog_logger()
|
||||
|
||||
|
||||
def _check_type_string(x):
|
||||
"""
|
||||
:param x:
|
||||
:return: True if it is of type string
|
||||
"""
|
||||
if isinstance(x, str):
|
||||
return True
|
||||
if sys.version_info[0] < 3:
|
||||
try:
|
||||
return isinstance(x, unicode)
|
||||
except NameError:
|
||||
return False
|
||||
|
||||
|
||||
class AviCheckModeResponse(object):
|
||||
"""
|
||||
Class to support ansible check mode.
|
||||
"""
|
||||
|
||||
def __init__(self, obj, status_code=200):
|
||||
self.obj = obj
|
||||
self.status_code = status_code
|
||||
|
||||
def json(self):
|
||||
return self.obj
|
||||
|
||||
|
||||
def ansible_return(module, rsp, changed, req=None, existing_obj=None,
|
||||
api_context=None):
|
||||
"""
|
||||
:param module: AnsibleModule
|
||||
:param rsp: ApiResponse from avi_api
|
||||
:param changed: boolean
|
||||
:param req: ApiRequest to avi_api
|
||||
:param existing_obj: object to be passed debug output
|
||||
:param api_context: api login context
|
||||
|
||||
helper function to return the right ansible based on the error code and
|
||||
changed
|
||||
Returns: specific ansible module exit function
|
||||
"""
|
||||
|
||||
if rsp is not None and rsp.status_code > 299:
|
||||
return module.fail_json(
|
||||
msg='Error %d Msg %s req: %s api_context:%s ' % (
|
||||
rsp.status_code, rsp.text, req, api_context))
|
||||
api_creds = AviCredentials()
|
||||
api_creds.update_from_ansible_module(module)
|
||||
key = '%s:%s:%s' % (api_creds.controller, api_creds.username,
|
||||
api_creds.port)
|
||||
disable_fact = module.params.get('avi_disable_session_cache_as_fact')
|
||||
|
||||
fact_context = None
|
||||
if not disable_fact:
|
||||
fact_context = module.params.get('api_context', {})
|
||||
if fact_context:
|
||||
fact_context.update({key: api_context})
|
||||
else:
|
||||
fact_context = {key: api_context}
|
||||
|
||||
obj_val = rsp.json() if rsp else existing_obj
|
||||
|
||||
if (obj_val and module.params.get("obj_username", None) and
|
||||
"username" in obj_val):
|
||||
obj_val["obj_username"] = obj_val["username"]
|
||||
if (obj_val and module.params.get("obj_password", None) and
|
||||
"password" in obj_val):
|
||||
obj_val["obj_password"] = obj_val["password"]
|
||||
old_obj_val = existing_obj if changed and existing_obj else None
|
||||
api_context_val = api_context if disable_fact else None
|
||||
ansible_facts_val = dict(
|
||||
avi_api_context=fact_context) if not disable_fact else {}
|
||||
|
||||
return module.exit_json(
|
||||
changed=changed, obj=obj_val, old_obj=old_obj_val,
|
||||
ansible_facts=ansible_facts_val, api_context=api_context_val)
|
||||
|
||||
|
||||
def purge_optional_fields(obj, module):
|
||||
"""
|
||||
It purges the optional arguments to be sent to the controller.
|
||||
:param obj: dictionary of the ansible object passed as argument.
|
||||
:param module: AnsibleModule
|
||||
return modified obj
|
||||
"""
|
||||
purge_fields = []
|
||||
for param, spec in module.argument_spec.items():
|
||||
if not spec.get('required', False):
|
||||
if param not in obj:
|
||||
# these are ansible common items
|
||||
continue
|
||||
if obj[param] is None:
|
||||
purge_fields.append(param)
|
||||
log.debug('purging fields %s', purge_fields)
|
||||
for param in purge_fields:
|
||||
obj.pop(param, None)
|
||||
return obj
|
||||
|
||||
|
||||
def cleanup_absent_fields(obj):
|
||||
"""
|
||||
cleans up any field that is marked as state: absent. It needs to be removed
|
||||
from the object if it is present.
|
||||
:param obj:
|
||||
:return: Purged object
|
||||
"""
|
||||
if type(obj) != dict:
|
||||
return obj
|
||||
cleanup_keys = []
|
||||
for k, v in obj.items():
|
||||
if type(v) == dict:
|
||||
if (('state' in v and v['state'] == 'absent') or
|
||||
(v == "{'state': 'absent'}")):
|
||||
cleanup_keys.append(k)
|
||||
else:
|
||||
cleanup_absent_fields(v)
|
||||
if not v:
|
||||
cleanup_keys.append(k)
|
||||
elif type(v) == list:
|
||||
new_list = []
|
||||
for elem in v:
|
||||
elem = cleanup_absent_fields(elem)
|
||||
if elem:
|
||||
# remove the item from list
|
||||
new_list.append(elem)
|
||||
if new_list:
|
||||
obj[k] = new_list
|
||||
else:
|
||||
cleanup_keys.append(k)
|
||||
elif isinstance(v, str) or isinstance(v, str):
|
||||
if v == "{'state': 'absent'}":
|
||||
cleanup_keys.append(k)
|
||||
for k in cleanup_keys:
|
||||
del obj[k]
|
||||
return obj
|
||||
|
||||
|
||||
RE_REF_MATCH = re.compile(r'^/api/[\w/]+\?name\=[\w]+[^#<>]*$')
|
||||
# if HTTP ref match then strip out the #name
|
||||
HTTP_REF_MATCH = re.compile(r'https://[\w.0-9:-]+/api/.+')
|
||||
HTTP_REF_W_NAME_MATCH = re.compile(r'https://[\w.0-9:-]+/api/.*#.+')
|
||||
|
||||
|
||||
def ref_n_str_cmp(x, y):
|
||||
"""
|
||||
compares two references
|
||||
1. check for exact reference
|
||||
2. check for obj_type/uuid
|
||||
3. check for name
|
||||
|
||||
if x is ref=name then extract uuid and name from y and use it.
|
||||
if x is http_ref then
|
||||
strip x and y
|
||||
compare them.
|
||||
|
||||
if x and y are urls then match with split on #
|
||||
if x is a RE_REF_MATCH then extract name
|
||||
if y is a REF_MATCH then extract name
|
||||
:param x: first string
|
||||
:param y: second string from controller's object
|
||||
|
||||
Returns
|
||||
True if they are equivalent else False
|
||||
"""
|
||||
if type(y) in (int, float, bool, int, complex):
|
||||
y = str(y)
|
||||
x = str(x)
|
||||
if not (_check_type_string(x) and _check_type_string(y)):
|
||||
return False
|
||||
y_uuid = y_name = str(y)
|
||||
x = str(x)
|
||||
if RE_REF_MATCH.match(x):
|
||||
x = x.split('name=')[1]
|
||||
elif HTTP_REF_MATCH.match(x):
|
||||
x = x.rsplit('#', 1)[0]
|
||||
y = y.rsplit('#', 1)[0]
|
||||
elif RE_REF_MATCH.match(y):
|
||||
y = y.split('name=')[1]
|
||||
|
||||
if HTTP_REF_W_NAME_MATCH.match(y):
|
||||
path = y.split('api/', 1)[1]
|
||||
# Fetching name or uuid from path /xxxx_xx/xx/xx_x/uuid_or_name
|
||||
uuid_or_name = path.split('/')[-1]
|
||||
parts = uuid_or_name.rsplit('#', 1)
|
||||
y_uuid = parts[0]
|
||||
y_name = parts[1] if len(parts) > 1 else ''
|
||||
# is just string but y is a url so match either uuid or name
|
||||
result = (x in (y, y_name, y_uuid))
|
||||
if not result:
|
||||
log.debug('x: %s y: %s y_name %s y_uuid %s',
|
||||
x, y, y_name, y_uuid)
|
||||
return result
|
||||
|
||||
|
||||
def avi_obj_cmp(x, y, sensitive_fields=None):
|
||||
"""
|
||||
compares whether x is fully contained in y. The comparision is different
|
||||
from a simple dictionary compare for following reasons
|
||||
1. Some fields could be references. The object in controller returns the
|
||||
full URL for those references. However, the ansible script would have
|
||||
it specified as /api/pool?name=blah. So, the reference fields need
|
||||
to match uuid, relative reference based on name and actual reference.
|
||||
|
||||
2. Optional fields with defaults: In case there are optional fields with
|
||||
defaults then controller automatically fills it up. This would
|
||||
cause the comparison with Ansible object specification to always return
|
||||
changed.
|
||||
|
||||
3. Optional fields without defaults: This is most tricky. The issue is
|
||||
how to specify deletion of such objects from ansible script. If the
|
||||
ansible playbook has object specified as Null then Avi controller will
|
||||
reject for non Message(dict) type fields. In addition, to deal with the
|
||||
defaults=null issue all the fields that are set with None are purged
|
||||
out before comparing with Avi controller's version
|
||||
|
||||
So, the solution is to pass state: absent if any optional field needs
|
||||
to be deleted from the configuration. The script would return changed
|
||||
=true if it finds a key in the controller version and it is marked with
|
||||
state: absent in ansible playbook. Alternatively, it would return
|
||||
false if key is not present in the controller object. Before, doing
|
||||
put or post it would purge the fields that are marked state: absent.
|
||||
|
||||
:param x: first string
|
||||
:param y: second string from controller's object
|
||||
:param sensitive_fields: sensitive fields to ignore for diff
|
||||
|
||||
Returns:
|
||||
True if x is subset of y else False
|
||||
"""
|
||||
if not sensitive_fields:
|
||||
sensitive_fields = set()
|
||||
if isinstance(x, str) or isinstance(x, str):
|
||||
# Special handling for strings as they can be references.
|
||||
return ref_n_str_cmp(x, y)
|
||||
if type(x) not in [list, dict]:
|
||||
# if it is not list or dict or string then simply compare the values
|
||||
return x == y
|
||||
if type(x) == list:
|
||||
# should compare each item in the list and that should match
|
||||
if len(x) != len(y):
|
||||
log.debug('x has %d items y has %d', len(x), len(y))
|
||||
return False
|
||||
for i in zip(x, y):
|
||||
if not avi_obj_cmp(i[0], i[1], sensitive_fields=sensitive_fields):
|
||||
# no need to continue
|
||||
return False
|
||||
|
||||
if type(x) == dict:
|
||||
x.pop('_last_modified', None)
|
||||
x.pop('tenant', None)
|
||||
y.pop('_last_modified', None)
|
||||
x.pop('api_version', None)
|
||||
y.pop('api_verison', None)
|
||||
d_xks = [k for k in x.keys() if k in sensitive_fields]
|
||||
|
||||
if d_xks:
|
||||
# if there is sensitive field then always return changed
|
||||
return False
|
||||
# pop the keys that are marked deleted but not present in y
|
||||
# return false if item is marked absent and is present in y
|
||||
d_x_absent_ks = []
|
||||
for k, v in x.items():
|
||||
if v is None:
|
||||
d_x_absent_ks.append(k)
|
||||
continue
|
||||
if isinstance(v, dict):
|
||||
if ('state' in v) and (v['state'] == 'absent'):
|
||||
if type(y) == dict and k not in y:
|
||||
d_x_absent_ks.append(k)
|
||||
else:
|
||||
return False
|
||||
elif not v:
|
||||
d_x_absent_ks.append(k)
|
||||
elif isinstance(v, list) and not v:
|
||||
d_x_absent_ks.append(k)
|
||||
# Added condition to check key in dict.
|
||||
elif isinstance(v, str) or (k in y and isinstance(y[k], str)):
|
||||
# this is the case when ansible converts the dictionary into a
|
||||
# string.
|
||||
if v == "{'state': 'absent'}" and k not in y:
|
||||
d_x_absent_ks.append(k)
|
||||
elif not v and k not in y:
|
||||
# this is the case when x has set the value that qualifies
|
||||
# as not but y does not have that value
|
||||
d_x_absent_ks.append(k)
|
||||
for k in d_x_absent_ks:
|
||||
x.pop(k)
|
||||
x_keys = set(x.keys())
|
||||
y_keys = set(y.keys())
|
||||
if not x_keys.issubset(y_keys):
|
||||
# log.debug('x has %s and y has %s keys', len(x_keys), len(y_keys))
|
||||
return False
|
||||
for k, v in x.items():
|
||||
if k not in y:
|
||||
# log.debug('k %s is not in y %s', k, y)
|
||||
return False
|
||||
if not avi_obj_cmp(v, y[k], sensitive_fields=sensitive_fields):
|
||||
# log.debug('k %s v %s did not match in y %s', k, v, y[k])
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
POP_FIELDS = ['state', 'controller', 'username', 'password', 'api_version',
|
||||
'avi_credentials', 'avi_api_update_method', 'avi_api_patch_op',
|
||||
'api_context', 'tenant', 'tenant_uuid', 'avi_disable_session_cache_as_fact']
|
||||
|
||||
|
||||
def get_api_context(module, api_creds):
|
||||
api_context = module.params.get('api_context')
|
||||
if api_context and module.params.get('avi_disable_session_cache_as_fact'):
|
||||
return api_context
|
||||
elif api_context and not module.params.get(
|
||||
'avi_disable_session_cache_as_fact'):
|
||||
key = '%s:%s:%s' % (api_creds.controller, api_creds.username,
|
||||
api_creds.port)
|
||||
return api_context.get(key)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
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())
|
||||
|
||||
|
||||
def avi_common_argument_spec():
|
||||
"""
|
||||
Returns common arguments for all Avi modules
|
||||
:return: dict
|
||||
"""
|
||||
return dict(
|
||||
controller=dict(default=os.environ.get('AVI_CONTROLLER', '')),
|
||||
username=dict(default=os.environ.get('AVI_USERNAME', '')),
|
||||
password=dict(default=os.environ.get('AVI_PASSWORD', ''), no_log=True),
|
||||
tenant=dict(default='admin'),
|
||||
tenant_uuid=dict(default=''),
|
||||
api_version=dict(default='16.4.4', type='str'),
|
||||
avi_credentials=dict(default=None, no_log=True, type='dict'),
|
||||
api_context=dict(type='dict'),
|
||||
avi_disable_session_cache_as_fact=dict(default=False, type='bool'))
|
@ -0,0 +1,972 @@
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import sys
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from ssl import SSLError
|
||||
|
||||
|
||||
class MockResponse(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
raise Exception("Requests library Response object not found. Using fake one.")
|
||||
|
||||
|
||||
class MockRequestsConnectionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MockSession(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
raise Exception("Requests library Session object not found. Using fake one.")
|
||||
|
||||
|
||||
HAS_AVI = True
|
||||
try:
|
||||
from requests import ConnectionError as RequestsConnectionError
|
||||
from requests import Response
|
||||
from requests.sessions import Session
|
||||
except ImportError:
|
||||
HAS_AVI = False
|
||||
Response = MockResponse
|
||||
RequestsConnectionError = MockRequestsConnectionError
|
||||
Session = MockSession
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
sessionDict = {}
|
||||
|
||||
|
||||
def avi_timedelta(td):
|
||||
'''
|
||||
This is a wrapper class to workaround python 2.6 builtin datetime.timedelta
|
||||
does not have total_seconds method
|
||||
:param timedelta object
|
||||
'''
|
||||
if type(td) != timedelta:
|
||||
raise TypeError()
|
||||
if sys.version_info >= (2, 7):
|
||||
ts = td.total_seconds()
|
||||
else:
|
||||
ts = td.seconds + (24 * 3600 * td.days)
|
||||
return ts
|
||||
|
||||
|
||||
def avi_sdk_syslog_logger(logger_name='avi.sdk'):
|
||||
# The following sets up syslog module to log underlying avi SDK messages
|
||||
# based on the environment variables:
|
||||
# AVI_LOG_HANDLER: names the logging handler to use. Only syslog is
|
||||
# supported.
|
||||
# AVI_LOG_LEVEL: Logging level used for the avi SDK. Default is DEBUG
|
||||
# AVI_SYSLOG_ADDRESS: Destination address for the syslog handler.
|
||||
# Default is /dev/log
|
||||
from logging.handlers import SysLogHandler
|
||||
lf = '[%(asctime)s] %(levelname)s [%(module)s.%(funcName)s:%(lineno)d] %(message)s'
|
||||
log = logging.getLogger(logger_name)
|
||||
log_level = os.environ.get('AVI_LOG_LEVEL', 'DEBUG')
|
||||
if log_level:
|
||||
log.setLevel(getattr(logging, log_level))
|
||||
formatter = logging.Formatter(lf)
|
||||
sh = SysLogHandler(address=os.environ.get('AVI_SYSLOG_ADDRESS', '/dev/log'))
|
||||
sh.setFormatter(formatter)
|
||||
log.addHandler(sh)
|
||||
return log
|
||||
|
||||
|
||||
class ObjectNotFound(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
def __init__(self, arg, rsp=None):
|
||||
self.args = [arg, rsp]
|
||||
self.rsp = rsp
|
||||
|
||||
|
||||
class AviServerError(APIError):
|
||||
def __init__(self, arg, rsp=None):
|
||||
super(AviServerError, self).__init__(arg, rsp)
|
||||
|
||||
|
||||
class APINotImplemented(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ApiResponse(Response):
|
||||
"""
|
||||
Returns copy of the requests.Response object provides additional helper
|
||||
routines
|
||||
1. obj: returns dictionary of Avi Object
|
||||
"""
|
||||
def __init__(self, rsp):
|
||||
super(ApiResponse, self).__init__()
|
||||
for k, v in list(rsp.__dict__.items()):
|
||||
setattr(self, k, v)
|
||||
|
||||
def json(self):
|
||||
"""
|
||||
Extends the session default json interface to handle special errors
|
||||
and raise Exceptions
|
||||
returns the Avi object as a dictionary from rsp.text
|
||||
"""
|
||||
if self.status_code in (200, 201):
|
||||
if not self.text:
|
||||
# In cases like status_code == 201 the response text could be
|
||||
# empty string.
|
||||
return None
|
||||
return super(ApiResponse, self).json()
|
||||
elif self.status_code == 204:
|
||||
# No response needed; e.g., delete operation
|
||||
return None
|
||||
elif self.status_code == 404:
|
||||
raise ObjectNotFound('HTTP Error: %s Error Msg %s' % (
|
||||
self.status_code, self.text), self)
|
||||
elif self.status_code >= 500:
|
||||
raise AviServerError('HTTP Error: %s Error Msg %s' % (
|
||||
self.status_code, self.text), self)
|
||||
else:
|
||||
raise APIError('HTTP Error: %s Error Msg %s' % (
|
||||
self.status_code, self.text), self)
|
||||
|
||||
def count(self):
|
||||
"""
|
||||
return the number of objects in the collection response. If it is not
|
||||
a collection response then it would simply return 1.
|
||||
"""
|
||||
obj = self.json()
|
||||
if 'count' in obj:
|
||||
# this was a resposne to collection
|
||||
return obj['count']
|
||||
return 1
|
||||
|
||||
@staticmethod
|
||||
def to_avi_response(resp):
|
||||
if type(resp) == Response:
|
||||
return ApiResponse(resp)
|
||||
return resp
|
||||
|
||||
|
||||
class AviCredentials(object):
|
||||
controller = ''
|
||||
username = ''
|
||||
password = ''
|
||||
api_version = '16.4.4'
|
||||
tenant = None
|
||||
tenant_uuid = None
|
||||
token = None
|
||||
port = None
|
||||
timeout = 300
|
||||
session_id = None
|
||||
csrftoken = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def update_from_ansible_module(self, m):
|
||||
"""
|
||||
:param m: ansible module
|
||||
:return:
|
||||
"""
|
||||
if m.params.get('avi_credentials'):
|
||||
for k, v in m.params['avi_credentials'].items():
|
||||
if hasattr(self, k):
|
||||
setattr(self, k, v)
|
||||
if m.params['controller']:
|
||||
self.controller = m.params['controller']
|
||||
if m.params['username']:
|
||||
self.username = m.params['username']
|
||||
if m.params['password']:
|
||||
self.password = m.params['password']
|
||||
if (m.params['api_version'] and
|
||||
(m.params['api_version'] != '16.4.4')):
|
||||
self.api_version = m.params['api_version']
|
||||
if m.params['tenant']:
|
||||
self.tenant = m.params['tenant']
|
||||
if m.params['tenant_uuid']:
|
||||
self.tenant_uuid = m.params['tenant_uuid']
|
||||
if m.params.get('session_id'):
|
||||
self.session_id = m.params['session_id']
|
||||
if m.params.get('csrftoken'):
|
||||
self.csrftoken = m.params['csrftoken']
|
||||
|
||||
def __str__(self):
|
||||
return 'controller %s user %s api %s tenant %s' % (
|
||||
self.controller, self.username, self.api_version, self.tenant)
|
||||
|
||||
|
||||
class ApiSession(Session):
|
||||
"""
|
||||
Extends the Request library's session object to provide helper
|
||||
utilities to work with Avi Controller like authentication, api massaging
|
||||
etc.
|
||||
"""
|
||||
|
||||
# This keeps track of the process which created the cache.
|
||||
# At anytime the pid of the process changes then it would create
|
||||
# a new cache for that process.
|
||||
AVI_SLUG = 'Slug'
|
||||
SESSION_CACHE_EXPIRY = 20 * 60
|
||||
SHARED_USER_HDRS = ['X-CSRFToken', 'Session-Id', 'Referer', 'Content-Type']
|
||||
MAX_API_RETRIES = 3
|
||||
|
||||
def __init__(self, controller_ip=None, username=None, password=None,
|
||||
token=None, tenant=None, tenant_uuid=None, verify=False,
|
||||
port=None, timeout=60, api_version=None,
|
||||
retry_conxn_errors=True, data_log=False,
|
||||
avi_credentials=None, session_id=None, csrftoken=None,
|
||||
lazy_authentication=False, max_api_retries=None):
|
||||
"""
|
||||
ApiSession takes ownership of avi_credentials and may update the
|
||||
information inside it.
|
||||
|
||||
Initialize new session object with authenticated token from login api.
|
||||
It also keeps a cache of user sessions that are cleaned up if inactive
|
||||
for more than 20 mins.
|
||||
|
||||
Notes:
|
||||
01. If mode is https and port is none or 443, we don't embed the
|
||||
port in the prefix. The prefix would be 'https://ip'. If port
|
||||
is a non-default value then we concatenate https://ip:port
|
||||
in the prefix.
|
||||
02. If mode is http and the port is none or 80, we don't embed the
|
||||
port in the prefix. The prefix would be 'http://ip'. If port is
|
||||
a non-default value, then we concatenate http://ip:port in
|
||||
the prefix.
|
||||
"""
|
||||
super(ApiSession, self).__init__()
|
||||
if not avi_credentials:
|
||||
tenant = tenant if tenant else "admin"
|
||||
self.avi_credentials = AviCredentials(
|
||||
controller=controller_ip, username=username, password=password,
|
||||
api_version=api_version, tenant=tenant, tenant_uuid=tenant_uuid,
|
||||
token=token, port=port, timeout=timeout,
|
||||
session_id=session_id, csrftoken=csrftoken)
|
||||
else:
|
||||
self.avi_credentials = avi_credentials
|
||||
self.headers = {}
|
||||
self.verify = verify
|
||||
self.retry_conxn_errors = retry_conxn_errors
|
||||
self.remote_api_version = {}
|
||||
self.session_cookie_name = ''
|
||||
self.user_hdrs = {}
|
||||
self.data_log = data_log
|
||||
self.num_session_retries = 0
|
||||
self.retry_wait_time = 0
|
||||
self.max_session_retries = (
|
||||
self.MAX_API_RETRIES if max_api_retries is None
|
||||
else int(max_api_retries))
|
||||
# Refer Notes 01 and 02
|
||||
k_port = port if port else 443
|
||||
if self.avi_credentials.controller.startswith('http'):
|
||||
k_port = 80 if not self.avi_credentials.port else k_port
|
||||
if self.avi_credentials.port is None or self.avi_credentials.port\
|
||||
== 80:
|
||||
self.prefix = self.avi_credentials.controller
|
||||
else:
|
||||
self.prefix = '{x}:{y}'.format(
|
||||
x=self.avi_credentials.controller,
|
||||
y=self.avi_credentials.port)
|
||||
else:
|
||||
if port is None or port == 443:
|
||||
self.prefix = 'https://{x}'.format(
|
||||
x=self.avi_credentials.controller)
|
||||
else:
|
||||
self.prefix = 'https://{x}:{y}'.format(
|
||||
x=self.avi_credentials.controller,
|
||||
y=self.avi_credentials.port)
|
||||
self.timeout = timeout
|
||||
self.key = '%s:%s:%s' % (self.avi_credentials.controller,
|
||||
self.avi_credentials.username, k_port)
|
||||
# Added api token and session id to sessionDict for handle single
|
||||
# session
|
||||
if self.avi_credentials.csrftoken:
|
||||
sessionDict[self.key] = {
|
||||
'api': self,
|
||||
"csrftoken": self.avi_credentials.csrftoken,
|
||||
"session_id": self.avi_credentials.session_id,
|
||||
"last_used": datetime.utcnow()
|
||||
}
|
||||
elif lazy_authentication:
|
||||
sessionDict.get(self.key, {}).update(
|
||||
{'api': self, "last_used": datetime.utcnow()})
|
||||
else:
|
||||
self.authenticate_session()
|
||||
|
||||
self.num_session_retries = 0
|
||||
self.pid = os.getpid()
|
||||
ApiSession._clean_inactive_sessions()
|
||||
return
|
||||
|
||||
@property
|
||||
def controller_ip(self):
|
||||
return self.avi_credentials.controller
|
||||
|
||||
@controller_ip.setter
|
||||
def controller_ip(self, controller_ip):
|
||||
self.avi_credentials.controller = controller_ip
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
return self.avi_credentials.username
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
return sessionDict.get(self.key, {}).get('connected', False)
|
||||
|
||||
@username.setter
|
||||
def username(self, username):
|
||||
self.avi_credentials.username = username
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
return self.avi_credentials.password
|
||||
|
||||
@password.setter
|
||||
def password(self, password):
|
||||
self.avi_credentials.password = password
|
||||
|
||||
@property
|
||||
def keystone_token(self):
|
||||
return sessionDict.get(self.key, {}).get('csrftoken', None)
|
||||
|
||||
@keystone_token.setter
|
||||
def keystone_token(self, token):
|
||||
sessionDict[self.key]['csrftoken'] = token
|
||||
|
||||
@property
|
||||
def tenant_uuid(self):
|
||||
self.avi_credentials.tenant_uuid
|
||||
|
||||
@tenant_uuid.setter
|
||||
def tenant_uuid(self, tenant_uuid):
|
||||
self.avi_credentials.tenant_uuid = tenant_uuid
|
||||
|
||||
@property
|
||||
def tenant(self):
|
||||
return self.avi_credentials.tenant
|
||||
|
||||
@tenant.setter
|
||||
def tenant(self, tenant):
|
||||
if tenant:
|
||||
self.avi_credentials.tenant = tenant
|
||||
else:
|
||||
self.avi_credentials.tenant = 'admin'
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
self.avi_credentials.port
|
||||
|
||||
@port.setter
|
||||
def port(self, port):
|
||||
self.avi_credentials.port = port
|
||||
|
||||
@property
|
||||
def api_version(self):
|
||||
return self.avi_credentials.api_version
|
||||
|
||||
@api_version.setter
|
||||
def api_version(self, api_version):
|
||||
self.avi_credentials.api_version = api_version
|
||||
|
||||
@property
|
||||
def session_id(self):
|
||||
return sessionDict[self.key]['session_id']
|
||||
|
||||
def get_context(self):
|
||||
return {
|
||||
'session_id': sessionDict[self.key]['session_id'],
|
||||
'csrftoken': sessionDict[self.key]['csrftoken']
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def clear_cached_sessions():
|
||||
global sessionDict
|
||||
sessionDict = {}
|
||||
|
||||
@staticmethod
|
||||
def get_session(
|
||||
controller_ip=None, username=None, password=None, token=None, tenant=None,
|
||||
tenant_uuid=None, verify=False, port=None, timeout=60,
|
||||
retry_conxn_errors=True, api_version=None, data_log=False,
|
||||
avi_credentials=None, session_id=None, csrftoken=None,
|
||||
lazy_authentication=False, max_api_retries=None):
|
||||
"""
|
||||
returns the session object for same user and tenant
|
||||
calls init if session dose not exist and adds it to session cache
|
||||
:param controller_ip: controller IP address
|
||||
:param username:
|
||||
:param password:
|
||||
:param token: Token to use; example, a valid keystone token
|
||||
:param tenant: Name of the tenant on Avi Controller
|
||||
:param tenant_uuid: Don't specify tenant when using tenant_id
|
||||
:param port: Rest-API may use a different port other than 443
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param retry_conxn_errors: retry on connection errors
|
||||
:param api_version: Controller API version
|
||||
"""
|
||||
if not avi_credentials:
|
||||
tenant = tenant if tenant else "admin"
|
||||
avi_credentials = AviCredentials(
|
||||
controller=controller_ip, username=username, password=password,
|
||||
api_version=api_version, tenant=tenant, tenant_uuid=tenant_uuid,
|
||||
token=token, port=port, timeout=timeout,
|
||||
session_id=session_id, csrftoken=csrftoken)
|
||||
|
||||
k_port = avi_credentials.port if avi_credentials.port else 443
|
||||
if avi_credentials.controller.startswith('http'):
|
||||
k_port = 80 if not avi_credentials.port else k_port
|
||||
key = '%s:%s:%s' % (avi_credentials.controller,
|
||||
avi_credentials.username, k_port)
|
||||
cached_session = sessionDict.get(key)
|
||||
if cached_session:
|
||||
user_session = cached_session['api']
|
||||
if not (user_session.avi_credentials.csrftoken or
|
||||
lazy_authentication):
|
||||
user_session.authenticate_session()
|
||||
else:
|
||||
user_session = ApiSession(
|
||||
controller_ip, username, password, token=token, tenant=tenant,
|
||||
tenant_uuid=tenant_uuid, verify=verify, port=port,
|
||||
timeout=timeout, retry_conxn_errors=retry_conxn_errors,
|
||||
api_version=api_version, data_log=data_log,
|
||||
avi_credentials=avi_credentials,
|
||||
lazy_authentication=lazy_authentication,
|
||||
max_api_retries=max_api_retries)
|
||||
ApiSession._clean_inactive_sessions()
|
||||
return user_session
|
||||
|
||||
def reset_session(self):
|
||||
"""
|
||||
resets and re-authenticates the current session.
|
||||
"""
|
||||
sessionDict[self.key]['connected'] = False
|
||||
logger.info('resetting session for %s', self.key)
|
||||
self.user_hdrs = {}
|
||||
for k, v in self.headers.items():
|
||||
if k not in self.SHARED_USER_HDRS:
|
||||
self.user_hdrs[k] = v
|
||||
self.headers = {}
|
||||
self.authenticate_session()
|
||||
|
||||
def authenticate_session(self):
|
||||
"""
|
||||
Performs session authentication with Avi controller and stores
|
||||
session cookies and sets header options like tenant.
|
||||
"""
|
||||
body = {"username": self.avi_credentials.username}
|
||||
if self.avi_credentials.password:
|
||||
body["password"] = self.avi_credentials.password
|
||||
elif self.avi_credentials.token:
|
||||
body["token"] = self.avi_credentials.token
|
||||
else:
|
||||
raise APIError("Neither user password or token provided")
|
||||
logger.debug('authenticating user %s prefix %s',
|
||||
self.avi_credentials.username, self.prefix)
|
||||
self.cookies.clear()
|
||||
err = None
|
||||
try:
|
||||
rsp = super(ApiSession, self).post(
|
||||
self.prefix + "/login", body, timeout=self.timeout, verify=self.verify)
|
||||
|
||||
if rsp.status_code == 200:
|
||||
self.num_session_retries = 0
|
||||
self.remote_api_version = rsp.json().get('version', {})
|
||||
self.session_cookie_name = rsp.json().get('session_cookie_name', 'sessionid')
|
||||
self.headers.update(self.user_hdrs)
|
||||
if rsp.cookies and 'csrftoken' in rsp.cookies:
|
||||
csrftoken = rsp.cookies['csrftoken']
|
||||
sessionDict[self.key] = {
|
||||
'csrftoken': csrftoken,
|
||||
'session_id': rsp.cookies[self.session_cookie_name],
|
||||
'last_used': datetime.utcnow(),
|
||||
'api': self,
|
||||
'connected': True
|
||||
}
|
||||
logger.debug("authentication success for user %s",
|
||||
self.avi_credentials.username)
|
||||
return
|
||||
# Check for bad request and invalid credentials response code
|
||||
elif rsp.status_code in [401, 403]:
|
||||
logger.error('Status Code %s msg %s', rsp.status_code, rsp.text)
|
||||
err = APIError('Status Code %s msg %s' % (
|
||||
rsp.status_code, rsp.text), rsp)
|
||||
raise err
|
||||
else:
|
||||
logger.error("Error status code %s msg %s", rsp.status_code,
|
||||
rsp.text)
|
||||
err = APIError('Status Code %s msg %s' % (
|
||||
rsp.status_code, rsp.text), rsp)
|
||||
except (RequestsConnectionError, SSLError) as e:
|
||||
if not self.retry_conxn_errors:
|
||||
raise
|
||||
logger.warning('Connection error retrying %s', e)
|
||||
err = e
|
||||
# comes here only if there was either exception or login was not
|
||||
# successful
|
||||
if self.retry_wait_time:
|
||||
time.sleep(self.retry_wait_time)
|
||||
self.num_session_retries += 1
|
||||
if self.num_session_retries > self.max_session_retries:
|
||||
self.num_session_retries = 0
|
||||
logger.error("giving up after %d retries connection failure %s",
|
||||
self.max_session_retries, True)
|
||||
ret_err = (
|
||||
err if err else APIError("giving up after %d retries connection failure %s" %
|
||||
(self.max_session_retries, True)))
|
||||
raise ret_err
|
||||
self.authenticate_session()
|
||||
return
|
||||
|
||||
def _get_api_headers(self, tenant, tenant_uuid, timeout, headers,
|
||||
api_version):
|
||||
"""
|
||||
returns the headers that are passed to the requests.Session api calls.
|
||||
"""
|
||||
api_hdrs = copy.deepcopy(self.headers)
|
||||
api_hdrs.update({
|
||||
"Referer": self.prefix,
|
||||
"Content-Type": "application/json"
|
||||
})
|
||||
api_hdrs['timeout'] = str(timeout)
|
||||
if self.key in sessionDict and 'csrftoken' in sessionDict.get(self.key):
|
||||
api_hdrs['X-CSRFToken'] = sessionDict.get(self.key)['csrftoken']
|
||||
else:
|
||||
self.authenticate_session()
|
||||
api_hdrs['X-CSRFToken'] = sessionDict.get(self.key)['csrftoken']
|
||||
if api_version:
|
||||
api_hdrs['X-Avi-Version'] = api_version
|
||||
elif self.avi_credentials.api_version:
|
||||
api_hdrs['X-Avi-Version'] = self.avi_credentials.api_version
|
||||
if tenant:
|
||||
tenant_uuid = None
|
||||
elif tenant_uuid:
|
||||
tenant = None
|
||||
else:
|
||||
tenant = self.avi_credentials.tenant
|
||||
tenant_uuid = self.avi_credentials.tenant_uuid
|
||||
if tenant_uuid:
|
||||
api_hdrs.update({"X-Avi-Tenant-UUID": "%s" % tenant_uuid})
|
||||
api_hdrs.pop("X-Avi-Tenant", None)
|
||||
elif tenant:
|
||||
api_hdrs.update({"X-Avi-Tenant": "%s" % tenant})
|
||||
api_hdrs.pop("X-Avi-Tenant-UUID", None)
|
||||
# Override any user headers that were passed by users. We don't know
|
||||
# when the user had updated the user_hdrs
|
||||
if self.user_hdrs:
|
||||
api_hdrs.update(self.user_hdrs)
|
||||
if headers:
|
||||
# overwrite the headers passed via the API calls.
|
||||
api_hdrs.update(headers)
|
||||
return api_hdrs
|
||||
|
||||
def _api(self, api_name, path, tenant, tenant_uuid, data=None,
|
||||
headers=None, timeout=None, api_version=None, **kwargs):
|
||||
"""
|
||||
It calls the requests.Session APIs and handles session expiry
|
||||
and other situations where session needs to be reset.
|
||||
returns ApiResponse object
|
||||
:param path: takes relative path to the AVI api.
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param headers: dictionary of headers that override the session
|
||||
headers.
|
||||
"""
|
||||
if self.pid != os.getpid():
|
||||
logger.info('pid %d change detected new %d. Closing session',
|
||||
self.pid, os.getpid())
|
||||
self.close()
|
||||
self.pid = os.getpid()
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
fullpath = self._get_api_path(path)
|
||||
fn = getattr(super(ApiSession, self), api_name)
|
||||
api_hdrs = self._get_api_headers(tenant, tenant_uuid, timeout, headers,
|
||||
api_version)
|
||||
connection_error = False
|
||||
err = None
|
||||
cookies = {
|
||||
'csrftoken': api_hdrs['X-CSRFToken'],
|
||||
}
|
||||
try:
|
||||
if self.session_cookie_name:
|
||||
cookies[self.session_cookie_name] = sessionDict[self.key]['session_id']
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
if (data is not None) and (type(data) == dict):
|
||||
resp = fn(fullpath, data=json.dumps(data), headers=api_hdrs,
|
||||
timeout=timeout, cookies=cookies, **kwargs)
|
||||
else:
|
||||
resp = fn(fullpath, data=data, headers=api_hdrs,
|
||||
timeout=timeout, cookies=cookies, **kwargs)
|
||||
except (RequestsConnectionError, SSLError) as e:
|
||||
logger.warning('Connection error retrying %s', e)
|
||||
if not self.retry_conxn_errors:
|
||||
raise
|
||||
connection_error = True
|
||||
err = e
|
||||
except Exception as e:
|
||||
logger.error('Error in Requests library %s', e)
|
||||
raise
|
||||
if not connection_error:
|
||||
logger.debug('path: %s http_method: %s hdrs: %s params: '
|
||||
'%s data: %s rsp: %s', fullpath, api_name.upper(),
|
||||
api_hdrs, kwargs, data,
|
||||
(resp.text if self.data_log else 'None'))
|
||||
if connection_error or resp.status_code in (401, 419):
|
||||
if connection_error:
|
||||
try:
|
||||
self.close()
|
||||
except Exception:
|
||||
# ignoring exception in cleanup path
|
||||
pass
|
||||
logger.warning('Connection failed, retrying.')
|
||||
# Adding sleep before retrying
|
||||
if self.retry_wait_time:
|
||||
time.sleep(self.retry_wait_time)
|
||||
else:
|
||||
logger.info('received error %d %s so resetting connection',
|
||||
resp.status_code, resp.text)
|
||||
ApiSession.reset_session(self)
|
||||
self.num_session_retries += 1
|
||||
if self.num_session_retries > self.max_session_retries:
|
||||
# Added this such that any code which re-tries can succeed
|
||||
# eventually.
|
||||
self.num_session_retries = 0
|
||||
if not connection_error:
|
||||
err = APIError('Status Code %s msg %s' % (
|
||||
resp.status_code, resp.text), resp)
|
||||
logger.error(
|
||||
"giving up after %d retries conn failure %s err %s",
|
||||
self.max_session_retries, connection_error, err)
|
||||
ret_err = (
|
||||
err if err else APIError("giving up after %d retries connection failure %s" %
|
||||
(self.max_session_retries, True)))
|
||||
raise ret_err
|
||||
# should restore the updated_hdrs to one passed down
|
||||
resp = self._api(api_name, path, tenant, tenant_uuid, data,
|
||||
headers=headers, api_version=api_version,
|
||||
timeout=timeout, **kwargs)
|
||||
self.num_session_retries = 0
|
||||
|
||||
if resp.cookies and 'csrftoken' in resp.cookies:
|
||||
csrftoken = resp.cookies['csrftoken']
|
||||
self.headers.update({"X-CSRFToken": csrftoken})
|
||||
self._update_session_last_used()
|
||||
return ApiResponse.to_avi_response(resp)
|
||||
|
||||
def get_controller_details(self):
|
||||
result = {
|
||||
"controller_ip": self.controller_ip,
|
||||
"controller_api_version": self.remote_api_version
|
||||
}
|
||||
return result
|
||||
|
||||
def get(self, path, tenant='', tenant_uuid='', timeout=None, params=None,
|
||||
api_version=None, **kwargs):
|
||||
"""
|
||||
It extends the Session Library interface to add AVI API prefixes,
|
||||
handle session exceptions related to authentication and update
|
||||
the global user session cache.
|
||||
:param path: takes relative path to the AVI api.
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param params: dictionary of key value pairs to be sent as query
|
||||
parameters
|
||||
:param api_version: overrides x-avi-header in request header during
|
||||
session creation
|
||||
get method takes relative path to service and kwargs as per Session
|
||||
class get method
|
||||
returns session's response object
|
||||
"""
|
||||
return self._api('get', path, tenant, tenant_uuid, timeout=timeout,
|
||||
params=params, api_version=api_version, **kwargs)
|
||||
|
||||
def get_object_by_name(self, path, name, tenant='', tenant_uuid='',
|
||||
timeout=None, params=None, api_version=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Helper function to access Avi REST Objects using object
|
||||
type and name. It behaves like python dictionary interface where it
|
||||
returns None when the object is not present in the AviController.
|
||||
Internally, it transforms the request to api/path?name=<name>...
|
||||
:param path: relative path to service
|
||||
:param name: name of the object
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param params: dictionary of key value pairs to be sent as query
|
||||
parameters
|
||||
:param api_version: overrides x-avi-header in request header during
|
||||
session creation
|
||||
returns dictionary object if successful else None
|
||||
"""
|
||||
obj = None
|
||||
if not params:
|
||||
params = {}
|
||||
params['name'] = name
|
||||
resp = self.get(path, tenant=tenant, tenant_uuid=tenant_uuid,
|
||||
timeout=timeout,
|
||||
params=params, api_version=api_version, **kwargs)
|
||||
if resp.status_code in (401, 419):
|
||||
ApiSession.reset_session(self)
|
||||
resp = self.get_object_by_name(
|
||||
path, name, tenant, tenant_uuid, timeout=timeout,
|
||||
params=params, **kwargs)
|
||||
if resp.status_code > 499 or 'Invalid version' in resp.text:
|
||||
logger.error('Error in get object by name for %s named %s. '
|
||||
'Error: %s', path, name, resp.text)
|
||||
raise AviServerError(resp.text, rsp=resp)
|
||||
elif resp.status_code > 299:
|
||||
return obj
|
||||
try:
|
||||
if 'results' in resp.json():
|
||||
obj = resp.json()['results'][0]
|
||||
else:
|
||||
# For apis returning single object eg. api/cluster
|
||||
obj = resp.json()
|
||||
except IndexError:
|
||||
logger.warning('Warning: Object Not found for %s named %s',
|
||||
path, name)
|
||||
obj = None
|
||||
self._update_session_last_used()
|
||||
return obj
|
||||
|
||||
def post(self, path, data=None, tenant='', tenant_uuid='', timeout=None,
|
||||
force_uuid=None, params=None, api_version=None, **kwargs):
|
||||
"""
|
||||
It extends the Session Library interface to add AVI API prefixes,
|
||||
handle session exceptions related to authentication and update
|
||||
the global user session cache.
|
||||
:param path: takes relative path to the AVI api.It is modified by
|
||||
the library to conform to AVI Controller's REST API interface
|
||||
:param data: dictionary of the data. Support for json string
|
||||
is deprecated
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param params: dictionary of key value pairs to be sent as query
|
||||
parameters
|
||||
:param api_version: overrides x-avi-header in request header during
|
||||
session creation
|
||||
returns session's response object
|
||||
"""
|
||||
if force_uuid is not None:
|
||||
headers = kwargs.get('headers', {})
|
||||
headers[self.AVI_SLUG] = force_uuid
|
||||
kwargs['headers'] = headers
|
||||
return self._api('post', path, tenant, tenant_uuid, data=data,
|
||||
timeout=timeout, params=params,
|
||||
api_version=api_version, **kwargs)
|
||||
|
||||
def put(self, path, data=None, tenant='', tenant_uuid='',
|
||||
timeout=None, params=None, api_version=None, **kwargs):
|
||||
"""
|
||||
It extends the Session Library interface to add AVI API prefixes,
|
||||
handle session exceptions related to authentication and update
|
||||
the global user session cache.
|
||||
:param path: takes relative path to the AVI api.It is modified by
|
||||
the library to conform to AVI Controller's REST API interface
|
||||
:param data: dictionary of the data. Support for json string
|
||||
is deprecated
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param params: dictionary of key value pairs to be sent as query
|
||||
parameters
|
||||
:param api_version: overrides x-avi-header in request header during
|
||||
session creation
|
||||
returns session's response object
|
||||
"""
|
||||
return self._api('put', path, tenant, tenant_uuid, data=data,
|
||||
timeout=timeout, params=params,
|
||||
api_version=api_version, **kwargs)
|
||||
|
||||
def patch(self, path, data=None, tenant='', tenant_uuid='',
|
||||
timeout=None, params=None, api_version=None, **kwargs):
|
||||
"""
|
||||
It extends the Session Library interface to add AVI API prefixes,
|
||||
handle session exceptions related to authentication and update
|
||||
the global user session cache.
|
||||
:param path: takes relative path to the AVI api.It is modified by
|
||||
the library to conform to AVI Controller's REST API interface
|
||||
:param data: dictionary of the data. Support for json string
|
||||
is deprecated
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param params: dictionary of key value pairs to be sent as query
|
||||
parameters
|
||||
:param api_version: overrides x-avi-header in request header during
|
||||
session creation
|
||||
returns session's response object
|
||||
"""
|
||||
return self._api('patch', path, tenant, tenant_uuid, data=data,
|
||||
timeout=timeout, params=params,
|
||||
api_version=api_version, **kwargs)
|
||||
|
||||
def put_by_name(self, path, name, data=None, tenant='',
|
||||
tenant_uuid='', timeout=None, params=None,
|
||||
api_version=None, **kwargs):
|
||||
"""
|
||||
Helper function to perform HTTP PUT on Avi REST Objects using object
|
||||
type and name.
|
||||
Internally, it transforms the request to api/path?name=<name>...
|
||||
:param path: relative path to service
|
||||
:param name: name of the object
|
||||
:param data: dictionary of the data. Support for json string
|
||||
is deprecated
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param params: dictionary of key value pairs to be sent as query
|
||||
parameters
|
||||
:param api_version: overrides x-avi-header in request header during
|
||||
session creation
|
||||
returns session's response object
|
||||
"""
|
||||
uuid = self._get_uuid_by_name(
|
||||
path, name, tenant, tenant_uuid, api_version=api_version)
|
||||
path = '%s/%s' % (path, uuid)
|
||||
return self.put(path, data, tenant, tenant_uuid, timeout=timeout,
|
||||
params=params, api_version=api_version, **kwargs)
|
||||
|
||||
def delete(self, path, tenant='', tenant_uuid='', timeout=None, params=None,
|
||||
data=None, api_version=None, **kwargs):
|
||||
"""
|
||||
It extends the Session Library interface to add AVI API prefixes,
|
||||
handle session exceptions related to authentication and update
|
||||
the global user session cache.
|
||||
:param path: takes relative path to the AVI api.It is modified by
|
||||
the library to conform to AVI Controller's REST API interface
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param params: dictionary of key value pairs to be sent as query
|
||||
parameters
|
||||
:param data: dictionary of the data. Support for json string
|
||||
is deprecated
|
||||
:param api_version: overrides x-avi-header in request header during
|
||||
session creation
|
||||
returns session's response object
|
||||
"""
|
||||
return self._api('delete', path, tenant, tenant_uuid, data=data,
|
||||
timeout=timeout, params=params,
|
||||
api_version=api_version, **kwargs)
|
||||
|
||||
def delete_by_name(self, path, name, tenant='', tenant_uuid='',
|
||||
timeout=None, params=None, api_version=None, **kwargs):
|
||||
"""
|
||||
Helper function to perform HTTP DELETE on Avi REST Objects using object
|
||||
type and name.Internally, it transforms the request to
|
||||
api/path?name=<name>...
|
||||
:param path: relative path to service
|
||||
:param name: name of the object
|
||||
:param tenant: overrides the tenant used during session creation
|
||||
:param tenant_uuid: overrides the tenant or tenant_uuid during session
|
||||
creation
|
||||
:param timeout: timeout for API calls; Default value is 60 seconds
|
||||
:param params: dictionary of key value pairs to be sent as query
|
||||
parameters
|
||||
:param api_version: overrides x-avi-header in request header during
|
||||
session creation
|
||||
returns session's response object
|
||||
"""
|
||||
uuid = self._get_uuid_by_name(path, name, tenant, tenant_uuid,
|
||||
api_version=api_version)
|
||||
if not uuid:
|
||||
raise ObjectNotFound("%s/?name=%s" % (path, name))
|
||||
path = '%s/%s' % (path, uuid)
|
||||
return self.delete(path, tenant, tenant_uuid, timeout=timeout,
|
||||
params=params, api_version=api_version, **kwargs)
|
||||
|
||||
def get_obj_ref(self, obj):
|
||||
"""returns reference url from dict object"""
|
||||
if not obj:
|
||||
return None
|
||||
if isinstance(obj, Response):
|
||||
obj = json.loads(obj.text)
|
||||
if obj.get(0, None):
|
||||
return obj[0]['url']
|
||||
elif obj.get('url', None):
|
||||
return obj['url']
|
||||
elif obj.get('results', None):
|
||||
return obj['results'][0]['url']
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_obj_uuid(self, obj):
|
||||
"""returns uuid from dict object"""
|
||||
if not obj:
|
||||
raise ObjectNotFound('Object %s Not found' % (obj))
|
||||
if isinstance(obj, Response):
|
||||
obj = json.loads(obj.text)
|
||||
if obj.get(0, None):
|
||||
return obj[0]['uuid']
|
||||
elif obj.get('uuid', None):
|
||||
return obj['uuid']
|
||||
elif obj.get('results', None):
|
||||
return obj['results'][0]['uuid']
|
||||
else:
|
||||
return None
|
||||
|
||||
def _get_api_path(self, path, uuid=None):
|
||||
"""
|
||||
This function returns the full url from relative path and uuid.
|
||||
"""
|
||||
if path == 'logout':
|
||||
return self.prefix + '/' + path
|
||||
elif uuid:
|
||||
return self.prefix + '/api/' + path + '/' + uuid
|
||||
else:
|
||||
return self.prefix + '/api/' + path
|
||||
|
||||
def _get_uuid_by_name(self, path, name, tenant='admin',
|
||||
tenant_uuid='', api_version=None):
|
||||
"""gets object by name and service path and returns uuid"""
|
||||
resp = self.get_object_by_name(
|
||||
path, name, tenant, tenant_uuid, api_version=api_version)
|
||||
if not resp:
|
||||
raise ObjectNotFound("%s/%s" % (path, name))
|
||||
return self.get_obj_uuid(resp)
|
||||
|
||||
def _update_session_last_used(self):
|
||||
if self.key in sessionDict:
|
||||
sessionDict[self.key]["last_used"] = datetime.utcnow()
|
||||
|
||||
@staticmethod
|
||||
def _clean_inactive_sessions():
|
||||
"""Removes sessions which are inactive more than 20 min"""
|
||||
session_cache = sessionDict
|
||||
logger.debug("cleaning inactive sessions in pid %d num elem %d",
|
||||
os.getpid(), len(session_cache))
|
||||
keys_to_delete = []
|
||||
for key, session in list(session_cache.items()):
|
||||
tdiff = avi_timedelta(datetime.utcnow() - session["last_used"])
|
||||
if tdiff < ApiSession.SESSION_CACHE_EXPIRY:
|
||||
continue
|
||||
keys_to_delete.append(key)
|
||||
for key in keys_to_delete:
|
||||
del session_cache[key]
|
||||
logger.debug("Removed session for : %s", key)
|
||||
|
||||
def delete_session(self):
|
||||
""" Removes the session for cleanup"""
|
||||
logger.debug("Removed session for : %s", self.key)
|
||||
sessionDict.pop(self.key, None)
|
||||
return
|
||||
# End of file
|
@ -1,117 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Created on Aug 25, 2016
|
||||
# @author: Gaurav Rastogi (grastogi@avinetworks.com)
|
||||
# Eric Anderson (eanderson@avinetworks.com)
|
||||
# module_check: supported
|
||||
# Avi Version: 17.1.1
|
||||
#
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: avi_gslbapplicationpersistenceprofile
|
||||
author: Gaurav Rastogi (@grastogi23) <grastogi@avinetworks.com>
|
||||
|
||||
short_description: Module for setup of GslbApplicationPersistenceProfile Avi RESTful Object
|
||||
description:
|
||||
- This module is used to configure GslbApplicationPersistenceProfile object
|
||||
- more examples at U(https://github.com/avinetworks/devops)
|
||||
requirements: [ avisdk ]
|
||||
version_added: "2.4"
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- The state that should be applied on the entity.
|
||||
default: present
|
||||
choices: ["absent","present"]
|
||||
description:
|
||||
description:
|
||||
- Field introduced in 17.1.1.
|
||||
name:
|
||||
description:
|
||||
- A user-friendly name for the persistence profile.
|
||||
- Field introduced in 17.1.1.
|
||||
required: true
|
||||
tenant_ref:
|
||||
description:
|
||||
- It is a reference to an object of type tenant.
|
||||
- Field introduced in 17.1.1.
|
||||
url:
|
||||
description:
|
||||
- Avi controller URL of the object.
|
||||
uuid:
|
||||
description:
|
||||
- Uuid of the persistence profile.
|
||||
- Field introduced in 17.1.1.
|
||||
extends_documentation_fragment:
|
||||
- avi
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Example to create GslbApplicationPersistenceProfile object
|
||||
avi_gslbapplicationpersistenceprofile:
|
||||
controller: 10.10.25.42
|
||||
username: admin
|
||||
password: something
|
||||
state: present
|
||||
name: sample_gslbapplicationpersistenceprofile
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
obj:
|
||||
description: GslbApplicationPersistenceProfile (api/gslbapplicationpersistenceprofile) object
|
||||
returned: success, changed
|
||||
type: dict
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
try:
|
||||
from ansible.module_utils.network.avi.avi import (
|
||||
avi_common_argument_spec, HAS_AVI, avi_ansible_api)
|
||||
except ImportError:
|
||||
HAS_AVI = False
|
||||
|
||||
|
||||
def main():
|
||||
argument_specs = dict(
|
||||
state=dict(default='present',
|
||||
choices=['absent', 'present']),
|
||||
description=dict(type='str',),
|
||||
name=dict(type='str', required=True),
|
||||
tenant_ref=dict(type='str',),
|
||||
url=dict(type='str',),
|
||||
uuid=dict(type='str',),
|
||||
)
|
||||
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) is not installed. '
|
||||
'For more details visit https://github.com/avinetworks/sdk.'))
|
||||
return avi_ansible_api(module, 'gslbapplicationpersistenceprofile',
|
||||
set([]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,178 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Created on Aug 25, 2016
|
||||
# @author: Gaurav Rastogi (grastogi@avinetworks.com)
|
||||
# Eric Anderson (eanderson@avinetworks.com)
|
||||
# module_check: supported
|
||||
# Avi Version: 17.1.1
|
||||
#
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: avi_gslbhealthmonitor
|
||||
author: Gaurav Rastogi (@grastogi23) <grastogi@avinetworks.com>
|
||||
|
||||
short_description: Module for setup of GslbHealthMonitor Avi RESTful Object
|
||||
description:
|
||||
- This module is used to configure GslbHealthMonitor object
|
||||
- more examples at U(https://github.com/avinetworks/devops)
|
||||
requirements: [ avisdk ]
|
||||
version_added: "2.4"
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- The state that should be applied on the entity.
|
||||
default: present
|
||||
choices: ["absent","present"]
|
||||
description:
|
||||
description:
|
||||
- User defined description for the object.
|
||||
dns_monitor:
|
||||
description:
|
||||
- Healthmonitordns settings for gslbhealthmonitor.
|
||||
external_monitor:
|
||||
description:
|
||||
- Healthmonitorexternal settings for gslbhealthmonitor.
|
||||
failed_checks:
|
||||
description:
|
||||
- Number of continuous failed health checks before the server is marked down.
|
||||
- Allowed values are 1-50.
|
||||
- Default value when not specified in API or module is interpreted by Avi Controller as 2.
|
||||
http_monitor:
|
||||
description:
|
||||
- Healthmonitorhttp settings for gslbhealthmonitor.
|
||||
https_monitor:
|
||||
description:
|
||||
- Healthmonitorhttp settings for gslbhealthmonitor.
|
||||
monitor_port:
|
||||
description:
|
||||
- Use this port instead of the port defined for the server in the pool.
|
||||
- If the monitor succeeds to this port, the load balanced traffic will still be sent to the port of the server defined within the pool.
|
||||
- Allowed values are 1-65535.
|
||||
- Special values are 0 - 'use server port'.
|
||||
name:
|
||||
description:
|
||||
- A user friendly name for this health monitor.
|
||||
required: true
|
||||
receive_timeout:
|
||||
description:
|
||||
- A valid response from the server is expected within the receive timeout window.
|
||||
- This timeout must be less than the send interval.
|
||||
- If server status is regularly flapping up and down, consider increasing this value.
|
||||
- Allowed values are 1-300.
|
||||
- Default value when not specified in API or module is interpreted by Avi Controller as 4.
|
||||
send_interval:
|
||||
description:
|
||||
- Frequency, in seconds, that monitors are sent to a server.
|
||||
- Allowed values are 1-3600.
|
||||
- Default value when not specified in API or module is interpreted by Avi Controller as 5.
|
||||
successful_checks:
|
||||
description:
|
||||
- Number of continuous successful health checks before server is marked up.
|
||||
- Allowed values are 1-50.
|
||||
- Default value when not specified in API or module is interpreted by Avi Controller as 2.
|
||||
tcp_monitor:
|
||||
description:
|
||||
- Healthmonitortcp settings for gslbhealthmonitor.
|
||||
tenant_ref:
|
||||
description:
|
||||
- It is a reference to an object of type tenant.
|
||||
type:
|
||||
description:
|
||||
- Type of the health monitor.
|
||||
- Enum options - HEALTH_MONITOR_PING, HEALTH_MONITOR_TCP, HEALTH_MONITOR_HTTP, HEALTH_MONITOR_HTTPS, HEALTH_MONITOR_EXTERNAL, HEALTH_MONITOR_UDP,
|
||||
- HEALTH_MONITOR_DNS, HEALTH_MONITOR_GSLB.
|
||||
required: true
|
||||
udp_monitor:
|
||||
description:
|
||||
- Healthmonitorudp settings for gslbhealthmonitor.
|
||||
url:
|
||||
description:
|
||||
- Avi controller URL of the object.
|
||||
uuid:
|
||||
description:
|
||||
- Uuid of the health monitor.
|
||||
extends_documentation_fragment:
|
||||
- avi
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Example to create GslbHealthMonitor object
|
||||
avi_gslbhealthmonitor:
|
||||
controller: 10.10.25.42
|
||||
username: admin
|
||||
password: something
|
||||
state: present
|
||||
name: sample_gslbhealthmonitor
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
obj:
|
||||
description: GslbHealthMonitor (api/gslbhealthmonitor) object
|
||||
returned: success, changed
|
||||
type: dict
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
try:
|
||||
from ansible.module_utils.network.avi.avi import (
|
||||
avi_common_argument_spec, HAS_AVI, avi_ansible_api)
|
||||
except ImportError:
|
||||
HAS_AVI = False
|
||||
|
||||
|
||||
def main():
|
||||
argument_specs = dict(
|
||||
state=dict(default='present',
|
||||
choices=['absent', 'present']),
|
||||
description=dict(type='str',),
|
||||
dns_monitor=dict(type='dict',),
|
||||
external_monitor=dict(type='dict',),
|
||||
failed_checks=dict(type='int',),
|
||||
http_monitor=dict(type='dict',),
|
||||
https_monitor=dict(type='dict',),
|
||||
monitor_port=dict(type='int',),
|
||||
name=dict(type='str', required=True),
|
||||
receive_timeout=dict(type='int',),
|
||||
send_interval=dict(type='int',),
|
||||
successful_checks=dict(type='int',),
|
||||
tcp_monitor=dict(type='dict',),
|
||||
tenant_ref=dict(type='str',),
|
||||
type=dict(type='str', required=True),
|
||||
udp_monitor=dict(type='dict',),
|
||||
url=dict(type='str',),
|
||||
uuid=dict(type='str',),
|
||||
)
|
||||
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) is not installed. '
|
||||
'For more details visit https://github.com/avinetworks/sdk.'))
|
||||
return avi_ansible_api(module, 'gslbhealthmonitor',
|
||||
set([]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,162 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# @author: Gaurav Rastogi (grastogi@avinetworks.com)
|
||||
# Eric Anderson (eanderson@avinetworks.com)
|
||||
# module_check: supported
|
||||
#
|
||||
# Copyright: (c) 2017 Gaurav Rastogi, <grastogi@avinetworks.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: avi_wafpolicy
|
||||
author: Gaurav Rastogi (@grastogi23) <grastogi@avinetworks.com>
|
||||
|
||||
short_description: Module for setup of WafPolicy Avi RESTful Object
|
||||
description:
|
||||
- This module is used to configure WafPolicy object
|
||||
- more examples at U(https://github.com/avinetworks/devops)
|
||||
requirements: [ avisdk ]
|
||||
version_added: "2.5"
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- The state that should be applied on the entity.
|
||||
default: present
|
||||
choices: ["absent", "present"]
|
||||
avi_api_update_method:
|
||||
description:
|
||||
- Default method for object update is HTTP PUT.
|
||||
- Setting to patch will override that behavior to use HTTP PATCH.
|
||||
version_added: "2.5"
|
||||
default: put
|
||||
choices: ["put", "patch"]
|
||||
avi_api_patch_op:
|
||||
description:
|
||||
- Patch operation to use when using avi_api_update_method as patch.
|
||||
version_added: "2.5"
|
||||
choices: ["add", "replace", "delete"]
|
||||
created_by:
|
||||
description:
|
||||
- Creator name.
|
||||
- Field introduced in 17.2.4.
|
||||
crs_groups:
|
||||
description:
|
||||
- Waf rules are categorized in to groups based on their characterization.
|
||||
- These groups are system created with crs groups.
|
||||
- Field introduced in 17.2.1.
|
||||
description:
|
||||
description:
|
||||
- Field introduced in 17.2.1.
|
||||
mode:
|
||||
description:
|
||||
- Waf policy mode.
|
||||
- This can be detection or enforcement.
|
||||
- Enum options - WAF_MODE_DETECTION_ONLY, WAF_MODE_ENFORCEMENT.
|
||||
- Field introduced in 17.2.1.
|
||||
- Default value when not specified in API or module is interpreted by Avi Controller as WAF_MODE_DETECTION_ONLY.
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- Field introduced in 17.2.1.
|
||||
required: true
|
||||
paranoia_level:
|
||||
description:
|
||||
- Waf ruleset paranoia mode.
|
||||
- This is used to select rules based on the paranoia-level tag.
|
||||
- Enum options - WAF_PARANOIA_LEVEL_LOW, WAF_PARANOIA_LEVEL_MEDIUM, WAF_PARANOIA_LEVEL_HIGH, WAF_PARANOIA_LEVEL_EXTREME.
|
||||
- Field introduced in 17.2.1.
|
||||
- Default value when not specified in API or module is interpreted by Avi Controller as WAF_PARANOIA_LEVEL_LOW.
|
||||
post_crs_groups:
|
||||
description:
|
||||
- Waf rules are categorized in to groups based on their characterization.
|
||||
- These groups are created by the user and will be enforced after the crs groups.
|
||||
- Field introduced in 17.2.1.
|
||||
pre_crs_groups:
|
||||
description:
|
||||
- Waf rules are categorized in to groups based on their characterization.
|
||||
- These groups are created by the user and will be enforced before the crs groups.
|
||||
- Field introduced in 17.2.1.
|
||||
tenant_ref:
|
||||
description:
|
||||
- It is a reference to an object of type tenant.
|
||||
- Field introduced in 17.2.1.
|
||||
url:
|
||||
description:
|
||||
- Avi controller URL of the object.
|
||||
uuid:
|
||||
description:
|
||||
- Field introduced in 17.2.1.
|
||||
waf_profile_ref:
|
||||
description:
|
||||
- Waf profile for waf policy.
|
||||
- It is a reference to an object of type wafprofile.
|
||||
- Field introduced in 17.2.1.
|
||||
required: true
|
||||
extends_documentation_fragment:
|
||||
- avi
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Example to create WafPolicy object
|
||||
avi_wafpolicy:
|
||||
controller: 10.10.25.42
|
||||
username: admin
|
||||
password: something
|
||||
state: present
|
||||
name: sample_wafpolicy
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
obj:
|
||||
description: WafPolicy (api/wafpolicy) object
|
||||
returned: success, changed
|
||||
type: dict
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
try:
|
||||
from ansible.module_utils.network.avi.avi import (
|
||||
avi_common_argument_spec, HAS_AVI, avi_ansible_api)
|
||||
except ImportError:
|
||||
HAS_AVI = False
|
||||
|
||||
|
||||
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']),
|
||||
created_by=dict(type='str',),
|
||||
crs_groups=dict(type='list',),
|
||||
description=dict(type='str',),
|
||||
mode=dict(type='str', required=True),
|
||||
name=dict(type='str', required=True),
|
||||
paranoia_level=dict(type='str',),
|
||||
post_crs_groups=dict(type='list',),
|
||||
pre_crs_groups=dict(type='list',),
|
||||
tenant_ref=dict(type='str',),
|
||||
url=dict(type='str',),
|
||||
uuid=dict(type='str',),
|
||||
waf_profile_ref=dict(type='str', required=True),
|
||||
)
|
||||
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) is not installed. '
|
||||
'For more details visit https://github.com/avinetworks/sdk.'))
|
||||
return avi_ansible_api(module, 'wafpolicy',
|
||||
set([]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,126 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# @author: Gaurav Rastogi (grastogi@avinetworks.com)
|
||||
# Eric Anderson (eanderson@avinetworks.com)
|
||||
# module_check: supported
|
||||
#
|
||||
# Copyright: (c) 2017 Gaurav Rastogi, <grastogi@avinetworks.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
#
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: avi_wafprofile
|
||||
author: Gaurav Rastogi (@grastogi23) <grastogi@avinetworks.com>
|
||||
|
||||
short_description: Module for setup of WafProfile Avi RESTful Object
|
||||
description:
|
||||
- This module is used to configure WafProfile object
|
||||
- more examples at U(https://github.com/avinetworks/devops)
|
||||
requirements: [ avisdk ]
|
||||
version_added: "2.5"
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- The state that should be applied on the entity.
|
||||
default: present
|
||||
choices: ["absent", "present"]
|
||||
avi_api_update_method:
|
||||
description:
|
||||
- Default method for object update is HTTP PUT.
|
||||
- Setting to patch will override that behavior to use HTTP PATCH.
|
||||
version_added: "2.5"
|
||||
default: put
|
||||
choices: ["put", "patch"]
|
||||
avi_api_patch_op:
|
||||
description:
|
||||
- Patch operation to use when using avi_api_update_method as patch.
|
||||
version_added: "2.5"
|
||||
choices: ["add", "replace", "delete"]
|
||||
config:
|
||||
description:
|
||||
- Config params for waf.
|
||||
- Field introduced in 17.2.1.
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- Field introduced in 17.2.1.
|
||||
files:
|
||||
description:
|
||||
- List of data files used for waf rules.
|
||||
- Field introduced in 17.2.1.
|
||||
name:
|
||||
description:
|
||||
- Field introduced in 17.2.1.
|
||||
required: true
|
||||
tenant_ref:
|
||||
description:
|
||||
- It is a reference to an object of type tenant.
|
||||
- Field introduced in 17.2.1.
|
||||
url:
|
||||
description:
|
||||
- Avi controller URL of the object.
|
||||
uuid:
|
||||
description:
|
||||
- Field introduced in 17.2.1.
|
||||
extends_documentation_fragment:
|
||||
- avi
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Example to create WafProfile object
|
||||
avi_wafprofile:
|
||||
controller: 10.10.25.42
|
||||
username: admin
|
||||
password: something
|
||||
state: present
|
||||
name: sample_wafprofile
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
obj:
|
||||
description: WafProfile (api/wafprofile) object
|
||||
returned: success, changed
|
||||
type: dict
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
try:
|
||||
from ansible.module_utils.network.avi.avi import (
|
||||
avi_common_argument_spec, HAS_AVI, avi_ansible_api)
|
||||
except ImportError:
|
||||
HAS_AVI = False
|
||||
|
||||
|
||||
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']),
|
||||
config=dict(type='dict', required=True),
|
||||
description=dict(type='str',),
|
||||
files=dict(type='list',),
|
||||
name=dict(type='str', required=True),
|
||||
tenant_ref=dict(type='str',),
|
||||
url=dict(type='str',),
|
||||
uuid=dict(type='str',),
|
||||
)
|
||||
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) is not installed. '
|
||||
'For more details visit https://github.com/avinetworks/sdk.'))
|
||||
return avi_ansible_api(module, 'wafprofile',
|
||||
set([]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue