New module: Issue NITRO requests to a Netscaler instance (network/netscaler/netscaler_nitro_request) (#33091)

* Add netscaler_nitro_request module

* Make changes as requested.

* Fix whitepsace in EXAMPLES block.

* Set module changed status according to operation performed.
This commit is contained in:
George Nikolopoulos 2017-11-28 16:16:01 +02:00 committed by John R Barker
parent 2114278947
commit 34f965addd
2 changed files with 1256 additions and 0 deletions

View file

@ -0,0 +1,909 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2017 Citrix Systems
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: netscaler_nitro_request
short_description: Issue Nitro API requests to a Netscaler instance.
description:
- Issue Nitro API requests to a Netscaler instance.
- This is intended to be a short hand for using the uri Ansible module to issue the raw HTTP requests directly.
- It provides consistent return values and has no other dependencies apart from the base Ansible runtime environment.
- This module is intended to run either on the Ansible control node or a bastion (jumpserver) with access to the actual Netscaler instance
version_added: "2.5.0"
author: George Nikolopoulos (@giorgos-nikolopoulos)
options:
nsip:
description:
- The IP address of the Netscaler or MAS instance where the Nitro API calls will be made.
- "The port can be specified with the colon C(:). E.g. C(192.168.1.1:555)."
nitro_user:
description:
- The username with which to authenticate to the Netscaler node.
required: true
nitro_pass:
description:
- The password with which to authenticate to the Netscaler node.
required: true
nitro_protocol:
choices: [ 'http', 'https' ]
default: http
description:
- Which protocol to use when accessing the Nitro API objects.
validate_certs:
description:
- If C(no), SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates.
default: 'yes'
nitro_auth_token:
description:
- The authentication token provided by the C(mas_login) operation. It is required when issuing Nitro API calls through a MAS proxy.
resource:
description:
- The type of resource we are operating on.
- It is required for all I(operation) values except C(mas_login) and C(save_config).
name:
description:
- The name of the resource we are operating on.
- "It is required for the following I(operation) values: C(update), C(get), C(delete)."
attributes:
description:
- The attributes of the Nitro object we are operating on.
- "It is required for the following I(operation) values: C(add), C(update), C(action)."
args:
description:
- A dictionary which defines the key arguments by which we will select the Nitro object to operate on.
- "It is required for the following I(operation) values: C(get_by_args), C('delete_by_args')."
filter:
description:
- A dictionary which defines the filter with which to refine the Nitro objects returned by the C(get_filtered) I(operation).
operation:
description:
- Define the Nitro operation that we want to perform.
choices:
- add
- update
- get
- get_by_args
- get_filtered
- get_all
- delete
- delete_by_args
- count
- mas_login
- save_config
- action
expected_nitro_errorcode:
description:
- A list of numeric values that signify that the operation was successful.
default: [0]
required: true
action:
description:
- The action to perform when the I(operation) value is set to C(action).
- Some common values for this parameter are C(enable), C(disable), C(rename).
instance_ip:
description:
- The IP address of the target Netscaler instance when issuing a Nitro request through a MAS proxy.
instance_name:
description:
- The name of the target Netscaler instance when issuing a Nitro request through a MAS proxy.
instance_id:
description:
- The id of the target Netscaler instance when issuing a Nitro request through a MAS proxy.
'''
EXAMPLES = '''
- name: Add a server
delegate_to: localhost
netscaler_nitro_request:
nsip: "{{ nsip }}"
nitro_user: "{{ nitro_user }}"
nitro_pass: "{{ nitro_pass }}"
operation: add
resource: server
name: test-server-1
attributes:
name: test-server-1
ipaddress: 192.168.1.1
- name: Update server
delegate_to: localhost
netscaler_nitro_request:
nsip: "{{ nsip }}"
nitro_user: "{{ nitro_user }}"
nitro_pass: "{{ nitro_pass }}"
operation: update
resource: server
name: test-server-1
attributes:
name: test-server-1
ipaddress: 192.168.1.2
- name: Get server
delegate_to: localhost
register: result
netscaler_nitro_request:
nsip: "{{ nsip }}"
nitro_user: "{{ nitro_user }}"
nitro_pass: "{{ nitro_pass }}"
operation: get
resource: server
name: test-server-1
- name: Delete server
delegate_to: localhost
register: result
netscaler_nitro_request:
nsip: "{{ nsip }}"
nitro_user: "{{ nitro_user }}"
nitro_pass: "{{ nitro_pass }}"
operation: delete
resource: server
name: test-server-1
- name: Rename server
delegate_to: localhost
netscaler_nitro_request:
nsip: "{{ nsip }}"
nitro_user: "{{ nitro_user }}"
nitro_pass: "{{ nitro_pass }}"
operation: action
action: rename
resource: server
attributes:
name: test-server-1
newname: test-server-2
- name: Get server by args
delegate_to: localhost
register: result
netscaler_nitro_request:
nsip: "{{ nsip }}"
nitro_user: "{{ nitro_user }}"
nitro_pass: "{{ nitro_pass }}"
operation: get_by_args
resource: server
args:
name: test-server-1
- name: Get server by filter
delegate_to: localhost
register: result
netscaler_nitro_request:
nsip: "{{ nsip }}"
nitro_user: "{{ nitro_user }}"
nitro_pass: "{{ nitro_pass }}"
operation: get_filtered
resource: server
filter:
ipaddress: 192.168.1.2
# Doing a NITRO request through MAS.
# Requires to have an authentication token from the mas_login and used as the nitro_auth_token parameter
# Also nsip is the MAS address and the target Netscaler IP must be defined with instance_ip
# The rest of the task arguments remain the same as when issuing the NITRO request directly to a Netscaler instance.
- name: Do mas login
delegate_to: localhost
register: login_result
netscaler_nitro_request:
nsip: "{{ mas_ip }}"
nitro_user: "{{ nitro_user }}"
nitro_pass: "{{ nitro_pass }}"
operation: mas_login
- name: Add resource through MAS proxy
delegate_to: localhost
netscaler_nitro_request:
nsip: "{{ mas_ip }}"
nitro_auth_token: "{{ login_result.nitro_auth_token }}"
instance_ip: "{{ nsip }}"
operation: add
resource: server
name: test-server-1
attributes:
name: test-server-1
ipaddress: 192.168.1.7
'''
RETURN = '''
nitro_errorcode:
description: A numeric value containing the return code of the NITRO operation. When 0 the operation is succesful. Any non zero value indicates an error.
returned: always
type: int
sample: 0
nitro_message:
description: A string containing a human readable explanation for the NITRO operation result.
returned: always
type: string
sample: Success
nitro_severity:
description: A string describing the severity of the NITRO operation error or NONE.
returned: always
type: string
sample: NONE
http_response_data:
description: A dictionary that contains all the HTTP response's data.
returned: always
type: dict
sample: "status: 200"
http_response_body:
description: A string with the actual HTTP response body content if existent. If there is no HTTP response body it is an empty string.
returned: always
type: string
sample: "{ errorcode: 0, message: Done, severity: NONE }"
nitro_object:
description: The object returned from the NITRO operation. This is applicable to the various get operations which return an object.
returned: when applicable
type: list
sample:
-
ipaddress: "192.168.1.8"
ipv6address: "NO"
maxbandwidth: "0"
name: "test-server-1"
port: 0
sp: "OFF"
state: "ENABLED"
nitro_auth_token:
description: The token returned by the C(mas_login) operation when succesful.
returned: when applicable
type: string
sample: "##E8D7D74DDBD907EE579E8BB8FF4529655F22227C1C82A34BFC93C9539D66"
'''
from ansible.module_utils.urls import fetch_url
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.basic import AnsibleModule
import codecs
class NitroAPICaller(object):
_argument_spec = dict(
nsip=dict(
fallback=(env_fallback, ['NETSCALER_NSIP']),
),
nitro_user=dict(
fallback=(env_fallback, ['NETSCALER_NITRO_USER']),
),
nitro_pass=dict(
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_auth_token=dict(
type='str',
no_log=True
),
resource=dict(type='str'),
name=dict(type='str'),
attributes=dict(type='dict'),
args=dict(type='dict'),
filter=dict(type='dict'),
operation=dict(
type='str',
required=True,
choices=[
'add',
'update',
'get',
'get_by_args',
'get_filtered',
'get_all',
'delete',
'delete_by_args',
'count',
'mas_login',
# Actions
'save_config',
# Generic action handler
'action',
]
),
expected_nitro_errorcode=dict(
type='list',
default=[0],
),
action=dict(type='str'),
instance_ip=dict(type='str'),
instance_name=dict(type='str'),
instance_id=dict(type='str'),
)
def __init__(self):
self._module = AnsibleModule(
argument_spec=self._argument_spec,
supports_check_mode=False,
)
self._module_result = dict(
failed=False,
)
# Prepare the http headers according to module arguments
self._headers = {}
self._headers['Content-Type'] = 'application/json'
# Check for conflicting authentication methods
have_token = self._module.params['nitro_auth_token'] is not None
have_userpass = None not in (self._module.params['nitro_user'], self._module.params['nitro_pass'])
login_operation = self._module.params['operation'] == 'mas_login'
if have_token and have_userpass:
self.fail_module(msg='Cannot define both authentication token and username/password')
if have_token:
self._headers['Cookie'] = "NITRO_AUTH_TOKEN=%s" % self._module.params['nitro_auth_token']
if have_userpass and not login_operation:
self._headers['X-NITRO-USER'] = self._module.params['nitro_user']
self._headers['X-NITRO-PASS'] = self._module.params['nitro_pass']
# Do header manipulation when doing a MAS proxy call
if self._module.params['instance_ip'] is not None:
self._headers['_MPS_API_PROXY_MANAGED_INSTANCE_IP'] = self._module.params['instance_ip']
elif self._module.params['instance_name'] is not None:
self._headers['_MPS_API_PROXY_MANAGED_INSTANCE_NAME'] = self._module.params['instance_name']
elif self._module.params['instance_id'] is not None:
self._headers['_MPS_API_PROXY_MANAGED_INSTANCE_ID'] = self._module.params['instance_id']
def edit_response_data(self, r, info, result, success_status):
# Search for body in both http body and http data
if r is not None:
result['http_response_body'] = codecs.decode(r.read(), 'utf-8')
elif 'body' in info:
result['http_response_body'] = codecs.decode(info['body'], 'utf-8')
del info['body']
else:
result['http_response_body'] = ''
result['http_response_data'] = info
# Update the nitro_* parameters according to expected success_status
# Use explicit return values from http response or deduce from http status code
# Nitro return code in http data
result['nitro_errorcode'] = None
result['nitro_message'] = None
result['nitro_severity'] = None
if result['http_response_body'] != '':
try:
data = self._module.from_json(result['http_response_body'])
except ValueError:
data = {}
result['nitro_errorcode'] = data.get('errorcode')
result['nitro_message'] = data.get('message')
result['nitro_severity'] = data.get('severity')
# If we do not have the nitro errorcode from body deduce it from the http status
if result['nitro_errorcode'] is None:
# HTTP status failed
if result['http_response_data'].get('status') != success_status:
result['nitro_errorcode'] = -1
result['nitro_message'] = result['http_response_data'].get('msg', 'HTTP status %s' % result['http_response_data']['status'])
result['nitro_severity'] = 'ERROR'
# HTTP status succeeded
else:
result['nitro_errorcode'] = 0
result['nitro_message'] = 'Success'
result['nitro_severity'] = 'NONE'
def handle_get_return_object(self, result):
result['nitro_object'] = []
if result['nitro_errorcode'] == 0:
if result['http_response_body'] != '':
data = self._module.from_json(result['http_response_body'])
if self._module.params['resource'] in data:
result['nitro_object'] = data[self._module.params['resource']]
else:
del result['nitro_object']
def fail_module(self, msg, **kwargs):
self._module_result['failed'] = True
self._module_result['changed'] = False
self._module_result.update(kwargs)
self._module_result['msg'] = msg
self._module.fail_json(**self._module_result)
def main(self):
if self._module.params['operation'] == 'add':
result = self.add()
if self._module.params['operation'] == 'update':
result = self.update()
if self._module.params['operation'] == 'delete':
result = self.delete()
if self._module.params['operation'] == 'delete_by_args':
result = self.delete_by_args()
if self._module.params['operation'] == 'get':
result = self.get()
if self._module.params['operation'] == 'get_by_args':
result = self.get_by_args()
if self._module.params['operation'] == 'get_filtered':
result = self.get_filtered()
if self._module.params['operation'] == 'get_all':
result = self.get_all()
if self._module.params['operation'] == 'count':
result = self.count()
if self._module.params['operation'] == 'mas_login':
result = self.mas_login()
if self._module.params['operation'] == 'action':
result = self.action()
if self._module.params['operation'] == 'save_config':
result = self.save_config()
if result['nitro_errorcode'] not in self._module.params['expected_nitro_errorcode']:
self.fail_module(msg='NITRO Failure', **result)
self._module_result.update(result)
self._module.exit_json(**self._module_result)
def exit_module(self):
self._module.exit_json()
def add(self):
# Check if required attributes are present
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
if self._module.params['attributes'] is None:
self.fail_module(msg='NITRO resource attributes are undefined.')
url = '%s://%s/nitro/v1/config/%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
)
data = self._module.jsonify({self._module.params['resource']: self._module.params['attributes']})
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
data=data,
method='POST',
)
result = {}
self.edit_response_data(r, info, result, success_status=201)
if result['nitro_errorcode'] == 0:
self._module_result['changed'] = True
else:
self._module_result['changed'] = False
return result
def update(self):
# Check if required attributes are arguments present
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
if self._module.params['name'] is None:
self.fail_module(msg='NITRO resource name is undefined.')
if self._module.params['attributes'] is None:
self.fail_module(msg='NITRO resource attributes are undefined.')
url = '%s://%s/nitro/v1/config/%s/%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
self._module.params['name'],
)
data = self._module.jsonify({self._module.params['resource']: self._module.params['attributes']})
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
data=data,
method='PUT',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
if result['nitro_errorcode'] == 0:
self._module_result['changed'] = True
else:
self._module_result['changed'] = False
return result
def get(self):
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
if self._module.params['name'] is None:
self.fail_module(msg='NITRO resource name is undefined.')
url = '%s://%s/nitro/v1/config/%s/%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
self._module.params['name'],
)
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
method='GET',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
self.handle_get_return_object(result)
self._module_result['changed'] = False
return result
def get_by_args(self):
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
if self._module.params['args'] is None:
self.fail_module(msg='NITRO args is undefined.')
url = '%s://%s/nitro/v1/config/%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
)
args_dict = self._module.params['args']
args = ','.join(['%s:%s' % (k, args_dict[k]) for k in args_dict])
args = 'args=' + args
url = '?'.join([url, args])
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
method='GET',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
self.handle_get_return_object(result)
self._module_result['changed'] = False
return result
def get_filtered(self):
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
if self._module.params['filter'] is None:
self.fail_module(msg='NITRO filter is undefined.')
keys = list(self._module.params['filter'].keys())
filter_key = keys[0]
filter_value = self._module.params['filter'][filter_key]
filter_str = '%s:%s' % (filter_key, filter_value)
url = '%s://%s/nitro/v1/config/%s?filter=%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
filter_str,
)
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
method='GET',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
self.handle_get_return_object(result)
self._module_result['changed'] = False
return result
def get_all(self):
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
url = '%s://%s/nitro/v1/config/%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
)
print('headers %s' % self._headers)
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
method='GET',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
self.handle_get_return_object(result)
self._module_result['changed'] = False
return result
def delete(self):
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
if self._module.params['name'] is None:
self.fail_module(msg='NITRO resource is undefined.')
# Deletion by name takes precedence over deletion by attributes
url = '%s://%s/nitro/v1/config/%s/%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
self._module.params['name'],
)
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
method='DELETE',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
if result['nitro_errorcode'] == 0:
self._module_result['changed'] = True
else:
self._module_result['changed'] = False
return result
def delete_by_args(self):
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
if self._module.params['args'] is None:
self.fail_module(msg='NITRO args is undefined.')
url = '%s://%s/nitro/v1/config/%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
)
args_dict = self._module.params['args']
args = ','.join(['%s:%s' % (k, args_dict[k]) for k in args_dict])
args = 'args=' + args
url = '?'.join([url, args])
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
method='DELETE',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
if result['nitro_errorcode'] == 0:
self._module_result['changed'] = True
else:
self._module_result['changed'] = False
return result
def count(self):
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
url = '%s://%s/nitro/v1/config/%s?count=yes' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
)
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
method='GET',
)
result = {}
self.edit_response_data(r, info, result)
if result['http_response_body'] != '':
data = self._module.from_json(result['http_response_body'])
result['nitro_errorcode'] = data['errorcode']
result['nitro_message'] = data['message']
result['nitro_severity'] = data['severity']
if self._module.params['resource'] in data:
result['nitro_count'] = data[self._module.params['resource']][0]['__count']
self._module_result['changed'] = False
return result
def action(self):
# Check if required attributes are present
if self._module.params['resource'] is None:
self.fail_module(msg='NITRO resource is undefined.')
if self._module.params['attributes'] is None:
self.fail_module(msg='NITRO resource attributes are undefined.')
if self._module.params['action'] is None:
self.fail_module(msg='NITRO action is undefined.')
url = '%s://%s/nitro/v1/config/%s?action=%s' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
self._module.params['resource'],
self._module.params['action'],
)
data = self._module.jsonify({self._module.params['resource']: self._module.params['attributes']})
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
data=data,
method='POST',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
if result['nitro_errorcode'] == 0:
self._module_result['changed'] = True
else:
self._module_result['changed'] = False
return result
def mas_login(self):
url = '%s://%s/nitro/v1/config/login' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
)
login_credentials = {
'login': {
'username': self._module.params['nitro_user'],
'password': self._module.params['nitro_pass'],
}
}
data = 'object=\n%s' % self._module.jsonify(login_credentials)
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
data=data,
method='POST',
)
print(r, info)
result = {}
self.edit_response_data(r, info, result, success_status=200)
if result['nitro_errorcode'] == 0:
body_data = self._module.from_json(result['http_response_body'])
result['nitro_auth_token'] = body_data['login'][0]['sessionid']
self._module_result['changed'] = False
return result
def save_config(self):
url = '%s://%s/nitro/v1/config/nsconfig?action=save' % (
self._module.params['nitro_protocol'],
self._module.params['nsip'],
)
data = self._module.jsonify(
{
'nsconfig': {},
}
)
r, info = fetch_url(
self._module,
url=url,
headers=self._headers,
data=data,
method='POST',
)
result = {}
self.edit_response_data(r, info, result, success_status=200)
self._module_result['changed'] = False
return result
def main():
nitro_api_caller = NitroAPICaller()
nitro_api_caller.main()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,347 @@
# Copyright (c) 2017 Citrix Systems
#
# 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/>.
#
from ansible.compat.tests.mock import patch, Mock, call
from .netscaler_module import TestModule
import copy
import tempfile
import json
import sys
import codecs
from ansible.modules.network.netscaler import netscaler_nitro_request
module_arguments = dict(
nsip=None,
nitro_user=None,
nitro_pass=None,
nitro_protocol=None,
validate_certs=None,
nitro_auth_token=None,
resource=None,
name=None,
attributes=None,
args=None,
filter=None,
operation=None,
expected_nitro_errorcode=None,
action=None,
instance_ip=None,
instance_name=None,
instance_id=None,
)
class TestNetscalerNitroRequestModule(TestModule):
@classmethod
def setUpClass(cls):
class MockException(Exception):
pass
cls.MockException = MockException
@classmethod
def tearDownClass(cls):
pass
def setUp(self):
pass
def tearDown(self):
pass
def test_fail_on_conflicting_authentication_methods(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_user='nsroot',
nitro_pass='nsroot',
nitro_auth_token='##DDASKLFDJ',
))
mock_module_instance = Mock(params=args)
expected_calls = [
call.fail_json(
changed=False,
failed=True,
msg='Cannot define both authentication token and username/password'
)
]
module_mock = Mock(return_value=mock_module_instance)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', module_mock):
netscaler_nitro_request.NitroAPICaller()
mock_module_instance.assert_has_calls(expected_calls)
def test_nitro_user_pass_credentials(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_user='nsroot',
nitro_pass='nsroot',
))
mock_module_instance = Mock(params=args)
expected_headers = {
'Content-Type': 'application/json',
'X-NITRO-USER': 'nsroot',
'X-NITRO-PASS': 'nsroot',
}
module_mock = Mock(return_value=mock_module_instance)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', module_mock):
instance = netscaler_nitro_request.NitroAPICaller()
self.assertDictEqual(instance._headers, expected_headers)
def test_mas_login_headers(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_user='nsroot',
nitro_pass='nsroot',
operation='mas_login',
))
mock_module_instance = Mock(params=args)
expected_headers = {
'Content-Type': 'application/json',
}
module_mock = Mock(return_value=mock_module_instance)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', module_mock):
instance = netscaler_nitro_request.NitroAPICaller()
self.assertDictEqual(instance._headers, expected_headers)
def test_mas_proxy_call_headers_instance_ip(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_auth_token='##ABDB',
operation='add',
instance_ip='192.168.1.1',
))
mock_module_instance = Mock(params=args)
expected_headers = {
'Content-Type': 'application/json',
'_MPS_API_PROXY_MANAGED_INSTANCE_IP': args['instance_ip'],
'Cookie': 'NITRO_AUTH_TOKEN=%s' % args['nitro_auth_token'],
}
module_mock = Mock(return_value=mock_module_instance)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', module_mock):
instance = netscaler_nitro_request.NitroAPICaller()
self.assertDictEqual(instance._headers, expected_headers)
def test_mas_proxy_call_headers_instance_id(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_auth_token='##ABDB',
operation='add',
instance_id='myid',
))
mock_module_instance = Mock(params=args)
expected_headers = {
'Content-Type': 'application/json',
'_MPS_API_PROXY_MANAGED_INSTANCE_ID': args['instance_id'],
'Cookie': 'NITRO_AUTH_TOKEN=%s' % args['nitro_auth_token'],
}
module_mock = Mock(return_value=mock_module_instance)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', module_mock):
instance = netscaler_nitro_request.NitroAPICaller()
self.assertDictEqual(instance._headers, expected_headers)
def test_mas_proxy_call_headers_instance_name(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_auth_token='##ABDB',
operation='add',
instance_name='myname',
))
mock_module_instance = Mock(params=args)
expected_headers = {
'Content-Type': 'application/json',
'_MPS_API_PROXY_MANAGED_INSTANCE_NAME': args['instance_name'],
'Cookie': 'NITRO_AUTH_TOKEN=%s' % args['nitro_auth_token'],
}
module_mock = Mock(return_value=mock_module_instance)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', module_mock):
instance = netscaler_nitro_request.NitroAPICaller()
self.assertDictEqual(instance._headers, expected_headers)
def test_edit_response_data_no_body_success_status(self):
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule'):
instance = netscaler_nitro_request.NitroAPICaller()
r = None
info = {
'status': 200,
}
result = {}
success_status = 200
expected_result = {
'nitro_errorcode': 0,
'nitro_message': 'Success',
'nitro_severity': 'NONE',
'http_response_body': '',
'http_response_data': info,
}
instance.edit_response_data(r, info, result, success_status)
self.assertDictEqual(result, expected_result)
def test_edit_response_data_no_body_fail_status(self):
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule'):
instance = netscaler_nitro_request.NitroAPICaller()
r = None
info = {
'status': 201,
}
result = {}
success_status = 200
expected_result = {
'nitro_errorcode': -1,
'nitro_message': 'HTTP status %s' % info['status'],
'nitro_severity': 'ERROR',
'http_response_body': '',
'http_response_data': info,
}
instance.edit_response_data(r, info, result, success_status)
self.assertDictEqual(result, expected_result)
def test_edit_response_data_actual_body_data(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_user='nsroot',
nitro_pass='nsroot',
nitro_auth_token='##DDASKLFDJ',
))
module_mock = Mock(params=args, from_json=json.loads)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', Mock(return_value=module_mock)):
with tempfile.TemporaryFile() as r:
actual_body = {
'errorcode': 258,
'message': 'Some error',
'severity': 'ERROR',
}
r.write(codecs.encode(json.dumps(actual_body), 'utf-8'))
r.seek(0)
instance = netscaler_nitro_request.NitroAPICaller()
info = {
'status': 200,
}
result = {}
success_status = 200
expected_result = {
'http_response_body': json.dumps(actual_body),
'http_response_data': info,
}
nitro_data = {}
for key, value in actual_body.items():
nitro_data['nitro_%s' % key] = value
expected_result.update(nitro_data)
instance.edit_response_data(r, info, result, success_status)
self.assertDictEqual(result, expected_result)
def test_edit_response_data_actual_body_data_irrelevant(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_user='nsroot',
nitro_pass='nsroot',
nitro_auth_token='##DDASKLFDJ',
))
module_mock = Mock(params=args, from_json=json.loads)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', Mock(return_value=module_mock)):
with tempfile.TemporaryFile() as r:
actual_body = {}
r.write(codecs.encode(json.dumps(actual_body), 'utf-8'))
r.seek(0)
instance = netscaler_nitro_request.NitroAPICaller()
info = {
'status': 200,
}
result = {}
success_status = 200
expected_result = {
'http_response_body': json.dumps(actual_body),
'http_response_data': info,
'nitro_errorcode': 0,
'nitro_message': 'Success',
'nitro_severity': 'NONE',
}
instance.edit_response_data(r, info, result, success_status)
self.assertDictEqual(result, expected_result)
def test_edit_response_data_body_in_info(self):
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_user='nsroot',
nitro_pass='nsroot',
))
module_mock = Mock(params=args, from_json=json.loads)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', Mock(return_value=module_mock)):
body = {
'errorcode': 258,
'message': 'Numerical error 258',
'severity': 'ERROR'
}
instance = netscaler_nitro_request.NitroAPICaller()
r = None
info = {
'status': 200,
'body': codecs.encode(json.dumps(body), 'utf-8'),
}
result = {}
success_status = 200
expected_result = {
'http_response_body': json.dumps(body),
'http_response_data': info,
}
nitro_data = {}
for key, value in body.items():
nitro_data['nitro_%s' % key] = value
expected_result.update(nitro_data)
instance.edit_response_data(r, info, result, success_status)
self.assertDictEqual(result, expected_result)
def test_handle_get_return_object(self):
resource = 'lbvserver'
args = copy.deepcopy(module_arguments)
args.update(dict(
nitro_user='nsroot',
nitro_pass='nsroot',
resource=resource,
))
resource_data = {
'property1': 'value1',
'property2': 'value2',
}
module_mock = Mock(params=args, from_json=json.loads)
with patch('ansible.modules.network.netscaler.netscaler_nitro_request.AnsibleModule', Mock(return_value=module_mock)):
instance = netscaler_nitro_request.NitroAPICaller()
data = {resource: resource_data}
result = {
'nitro_errorcode': 0,
'http_response_body': json.dumps(data),
}
expected_result = {
'nitro_object': resource_data
}
expected_result.update(result)
instance.handle_get_return_object(result)
self.assertDictEqual(result, expected_result)