mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-08-23 08:22:19 +02:00
323 lines
12 KiB
Python
323 lines
12 KiB
Python
![]() |
# -*- coding: utf-8 -*-
|
||
|
|
||
|
# Copyright (c) 2017 Citrix Systems
|
||
|
#
|
||
|
# This code is part of Ansible, but is an independent component.
|
||
|
# This particular file snippet, and this file snippet only, is BSD licensed.
|
||
|
# Modules you write using this snippet, which is embedded dynamically by Ansible
|
||
|
# still belong to the author of the module, and may assign their own license
|
||
|
# to the complete work.
|
||
|
#
|
||
|
# Redistribution and use in source and binary forms, with or without modification,
|
||
|
# are permitted provided that the following conditions are met:
|
||
|
#
|
||
|
# * Redistributions of source code must retain the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer.
|
||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||
|
# this list of conditions and the following disclaimer in the documentation
|
||
|
# and/or other materials provided with the distribution.
|
||
|
#
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||
|
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
#
|
||
|
|
||
|
import json
|
||
|
import re
|
||
|
import sys
|
||
|
|
||
|
from ansible.module_utils.basic import env_fallback
|
||
|
from ansible.module_utils.six import binary_type, text_type
|
||
|
from ansible.module_utils._text import to_native
|
||
|
|
||
|
|
||
|
class ConfigProxy(object):
|
||
|
|
||
|
def __init__(self, actual, client, attribute_values_dict, readwrite_attrs, transforms=None, readonly_attrs=None, immutable_attrs=None, json_encodes=None):
|
||
|
transforms = {} if transforms is None else transforms
|
||
|
readonly_attrs = [] if readonly_attrs is None else readonly_attrs
|
||
|
immutable_attrs = [] if immutable_attrs is None else immutable_attrs
|
||
|
json_encodes = [] if json_encodes is None else json_encodes
|
||
|
|
||
|
# Actual config object from nitro sdk
|
||
|
self.actual = actual
|
||
|
|
||
|
# nitro client
|
||
|
self.client = client
|
||
|
|
||
|
# ansible attribute_values_dict
|
||
|
self.attribute_values_dict = attribute_values_dict
|
||
|
|
||
|
self.readwrite_attrs = readwrite_attrs
|
||
|
self.readonly_attrs = readonly_attrs
|
||
|
self.immutable_attrs = immutable_attrs
|
||
|
self.json_encodes = json_encodes
|
||
|
self.transforms = transforms
|
||
|
|
||
|
self.attribute_values_processed = {}
|
||
|
for attribute, value in self.attribute_values_dict.items():
|
||
|
if value is None:
|
||
|
continue
|
||
|
if attribute in transforms:
|
||
|
for transform in self.transforms[attribute]:
|
||
|
if transform == 'bool_yes_no':
|
||
|
if value is True:
|
||
|
value = 'YES'
|
||
|
elif value is False:
|
||
|
value = 'NO'
|
||
|
elif transform == 'bool_on_off':
|
||
|
if value is True:
|
||
|
value = 'ON'
|
||
|
elif value is False:
|
||
|
value = 'OFF'
|
||
|
elif callable(transform):
|
||
|
value = transform(value)
|
||
|
else:
|
||
|
raise Exception('Invalid transform %s' % transform)
|
||
|
self.attribute_values_processed[attribute] = value
|
||
|
|
||
|
self._copy_attributes_to_actual()
|
||
|
|
||
|
def _copy_attributes_to_actual(self):
|
||
|
for attribute in self.readwrite_attrs:
|
||
|
if attribute in self.attribute_values_processed:
|
||
|
attribute_value = self.attribute_values_processed[attribute]
|
||
|
|
||
|
if attribute_value is None:
|
||
|
continue
|
||
|
|
||
|
# Fallthrough
|
||
|
if attribute in self.json_encodes:
|
||
|
attribute_value = json.JSONEncoder().encode(attribute_value).strip('"')
|
||
|
setattr(self.actual, attribute, attribute_value)
|
||
|
|
||
|
def __getattr__(self, name):
|
||
|
if name in self.attribute_values_dict:
|
||
|
return self.attribute_values_dict[name]
|
||
|
else:
|
||
|
raise AttributeError('No attribute %s found' % name)
|
||
|
|
||
|
def add(self):
|
||
|
self.actual.__class__.add(self.client, self.actual)
|
||
|
|
||
|
def update(self):
|
||
|
return self.actual.__class__.update(self.client, self.actual)
|
||
|
|
||
|
def delete(self):
|
||
|
self.actual.__class__.delete(self.client, self.actual)
|
||
|
|
||
|
def get(self, *args, **kwargs):
|
||
|
result = self.actual.__class__.get(self.client, *args, **kwargs)
|
||
|
|
||
|
return result
|
||
|
|
||
|
def has_equal_attributes(self, other):
|
||
|
if self.diff_object(other) == {}:
|
||
|
return True
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
def diff_object(self, other):
|
||
|
diff_dict = {}
|
||
|
for attribute in self.attribute_values_processed:
|
||
|
# Skip readonly attributes
|
||
|
if attribute not in self.readwrite_attrs:
|
||
|
continue
|
||
|
|
||
|
# Skip attributes not present in module arguments
|
||
|
if self.attribute_values_processed[attribute] is None:
|
||
|
continue
|
||
|
|
||
|
# Check existence
|
||
|
if hasattr(other, attribute):
|
||
|
attribute_value = getattr(other, attribute)
|
||
|
else:
|
||
|
diff_dict[attribute] = 'missing from other'
|
||
|
continue
|
||
|
|
||
|
# Compare values
|
||
|
param_type = self.attribute_values_processed[attribute].__class__
|
||
|
if attribute_value is None or param_type(attribute_value) != self.attribute_values_processed[attribute]:
|
||
|
str_tuple = (
|
||
|
type(self.attribute_values_processed[attribute]),
|
||
|
self.attribute_values_processed[attribute],
|
||
|
type(attribute_value),
|
||
|
attribute_value,
|
||
|
)
|
||
|
diff_dict[attribute] = 'difference. ours: (%s) %s other: (%s) %s' % str_tuple
|
||
|
return diff_dict
|
||
|
|
||
|
def get_actual_rw_attributes(self, filter='name'):
|
||
|
if self.actual.__class__.count_filtered(self.client, '%s:%s' % (filter, self.attribute_values_dict[filter])) == 0:
|
||
|
return {}
|
||
|
server_list = self.actual.__class__.get_filtered(self.client, '%s:%s' % (filter, self.attribute_values_dict[filter]))
|
||
|
actual_instance = server_list[0]
|
||
|
ret_val = {}
|
||
|
for attribute in self.readwrite_attrs:
|
||
|
if not hasattr(actual_instance, attribute):
|
||
|
continue
|
||
|
ret_val[attribute] = getattr(actual_instance, attribute)
|
||
|
return ret_val
|
||
|
|
||
|
def get_actual_ro_attributes(self, filter='name'):
|
||
|
if self.actual.__class__.count_filtered(self.client, '%s:%s' % (filter, self.attribute_values_dict[filter])) == 0:
|
||
|
return {}
|
||
|
server_list = self.actual.__class__.get_filtered(self.client, '%s:%s' % (filter, self.attribute_values_dict[filter]))
|
||
|
actual_instance = server_list[0]
|
||
|
ret_val = {}
|
||
|
for attribute in self.readonly_attrs:
|
||
|
if not hasattr(actual_instance, attribute):
|
||
|
continue
|
||
|
ret_val[attribute] = getattr(actual_instance, attribute)
|
||
|
return ret_val
|
||
|
|
||
|
def get_missing_rw_attributes(self):
|
||
|
return list(set(self.readwrite_attrs) - set(self.get_actual_rw_attributes().keys()))
|
||
|
|
||
|
def get_missing_ro_attributes(self):
|
||
|
return list(set(self.readonly_attrs) - set(self.get_actual_ro_attributes().keys()))
|
||
|
|
||
|
|
||
|
def get_immutables_intersection(config_proxy, keys):
|
||
|
immutables_set = set(config_proxy.immutable_attrs)
|
||
|
keys_set = set(keys)
|
||
|
# Return list of sets' intersection
|
||
|
return list(immutables_set & keys_set)
|
||
|
|
||
|
|
||
|
def ensure_feature_is_enabled(client, feature_str):
|
||
|
enabled_features = client.get_enabled_features()
|
||
|
|
||
|
if enabled_features is None:
|
||
|
enabled_features = []
|
||
|
|
||
|
if feature_str not in enabled_features:
|
||
|
client.enable_features(feature_str)
|
||
|
client.save_config()
|
||
|
|
||
|
|
||
|
def get_nitro_client(module):
|
||
|
from nssrc.com.citrix.netscaler.nitro.service.nitro_service import nitro_service
|
||
|
|
||
|
client = nitro_service(module.params['nsip'], module.params['nitro_protocol'])
|
||
|
client.set_credential(module.params['nitro_user'], module.params['nitro_pass'])
|
||
|
client.timeout = float(module.params['nitro_timeout'])
|
||
|
client.certvalidation = module.params['validate_certs']
|
||
|
return client
|
||
|
|
||
|
|
||
|
netscaler_common_arguments = dict(
|
||
|
nsip=dict(
|
||
|
required=True,
|
||
|
fallback=(env_fallback, ['NETSCALER_NSIP']),
|
||
|
),
|
||
|
nitro_user=dict(
|
||
|
required=True,
|
||
|
fallback=(env_fallback, ['NETSCALER_NITRO_USER']),
|
||
|
no_log=True
|
||
|
),
|
||
|
nitro_pass=dict(
|
||
|
required=True,
|
||
|
fallback=(env_fallback, ['NETSCALER_NITRO_PASS']),
|
||
|
no_log=True
|
||
|
),
|
||
|
nitro_protocol=dict(
|
||
|
choices=['http', 'https'],
|
||
|
fallback=(env_fallback, ['NETSCALER_NITRO_PROTOCOL']),
|
||
|
default='http'
|
||
|
),
|
||
|
validate_certs=dict(
|
||
|
default=True,
|
||
|
type='bool'
|
||
|
),
|
||
|
nitro_timeout=dict(default=310, type='float'),
|
||
|
state=dict(
|
||
|
choices=[
|
||
|
'present',
|
||
|
'absent',
|
||
|
],
|
||
|
default='present',
|
||
|
),
|
||
|
save_config=dict(
|
||
|
type='bool',
|
||
|
default=True,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
|
||
|
loglines = []
|
||
|
|
||
|
|
||
|
def complete_missing_attributes(actual, attrs_list, fill_value=None):
|
||
|
for attribute in attrs_list:
|
||
|
if not hasattr(actual, attribute):
|
||
|
setattr(actual, attribute, fill_value)
|
||
|
|
||
|
|
||
|
def log(msg):
|
||
|
loglines.append(msg)
|
||
|
|
||
|
|
||
|
def get_ns_version(client):
|
||
|
from nssrc.com.citrix.netscaler.nitro.resource.config.ns.nsversion import nsversion
|
||
|
result = nsversion.get(client)
|
||
|
m = re.match(r'^.*NS(\d+)\.(\d+).*$', result[0].version)
|
||
|
if m is None:
|
||
|
return None
|
||
|
else:
|
||
|
return int(m.group(1)), int(m.group(2))
|
||
|
|
||
|
|
||
|
def get_ns_hardware(client):
|
||
|
from nssrc.com.citrix.netscaler.nitro.resource.config.ns.nshardware import nshardware
|
||
|
result = nshardware.get(client)
|
||
|
return result
|
||
|
|
||
|
|
||
|
def monkey_patch_nitro_api():
|
||
|
|
||
|
from nssrc.com.citrix.netscaler.nitro.resource.base.Json import Json
|
||
|
|
||
|
def new_resource_to_string_convert(self, resrc):
|
||
|
# Line below is the actual patch
|
||
|
dict_valid_values = dict((k.replace('_', '', 1), v) for k, v in resrc.__dict__.items() if v)
|
||
|
return json.dumps(dict_valid_values)
|
||
|
Json.resource_to_string_convert = new_resource_to_string_convert
|
||
|
|
||
|
from nssrc.com.citrix.netscaler.nitro.util.nitro_util import nitro_util
|
||
|
|
||
|
@classmethod
|
||
|
def object_to_string_new(cls, obj):
|
||
|
output = []
|
||
|
flds = obj.__dict__
|
||
|
for k, v in ((k.replace('_', '', 1), v) for k, v in flds.items() if v):
|
||
|
if isinstance(v, bool):
|
||
|
output.append('"%s":%s' % (k, v))
|
||
|
elif isinstance(v, (binary_type, text_type)):
|
||
|
v = to_native(v, errors='surrogate_or_strict')
|
||
|
output.append('"%s":"%s"' % (k, v))
|
||
|
elif isinstance(v, int):
|
||
|
output.append('"%s":"%s"' % (k, v))
|
||
|
return ','.join(output)
|
||
|
|
||
|
@classmethod
|
||
|
def object_to_string_withoutquotes_new(cls, obj):
|
||
|
output = []
|
||
|
flds = obj.__dict__
|
||
|
for k, v in ((k.replace('_', '', 1), v) for k, v in flds.items() if v):
|
||
|
if isinstance(v, (int, bool)):
|
||
|
output.append('%s:%s' % (k, v))
|
||
|
elif isinstance(v, (binary_type, text_type)):
|
||
|
v = to_native(v, errors='surrogate_or_strict')
|
||
|
output.append('%s:%s' % (k, cls.encode(v)))
|
||
|
return ','.join(output)
|
||
|
|
||
|
nitro_util.object_to_string = object_to_string_new
|
||
|
nitro_util.object_to_string_withoutquotes = object_to_string_withoutquotes_new
|