From fd3ae0bf803838cf6373ced08105c040b0d08329 Mon Sep 17 00:00:00 2001 From: Ted Date: Tue, 3 Jan 2017 20:26:34 -0700 Subject: [PATCH] Add bigswitch big mon inline chain module (#18631) * Add bigswitch util * Add big switch big mon inline chain module * Add required to access_token doc * Add controller to doc * Add validate_certs to doc * Add author & metadata * Add BSD license header --- lib/ansible/module_utils/bigswitch_utils.py | 88 ++++++++++ .../modules/network/bigswitch/__init__.py | 0 .../modules/network/bigswitch/bigmon_chain.py | 159 ++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100755 lib/ansible/module_utils/bigswitch_utils.py create mode 100644 lib/ansible/modules/network/bigswitch/__init__.py create mode 100755 lib/ansible/modules/network/bigswitch/bigmon_chain.py diff --git a/lib/ansible/module_utils/bigswitch_utils.py b/lib/ansible/module_utils/bigswitch_utils.py new file mode 100755 index 00000000000..24a126220a9 --- /dev/null +++ b/lib/ansible/module_utils/bigswitch_utils.py @@ -0,0 +1,88 @@ +# 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. +# +# (c) 2016, Ted Elhourani +# +# +# 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 +from ansible.module_utils.urls import fetch_url + +class Response(object): + + def __init__(self, resp, info): + self.body = None + if resp: + self.body = resp.read() + self.info = info + + @property + def json(self): + if not self.body: + if "body" in self.info: + return json.loads(self.info["body"]) + return None + try: + return json.loads(self.body) + except ValueError: + return None + + @property + def status_code(self): + return self.info["status"] + + +class Rest(object): + + def __init__(self, module, headers, baseurl): + self.module = module + self.headers = headers + self.baseurl = baseurl + + def _url_builder(self, path): + if path[0] == '/': + path = path[1:] + return '%s/%s' % (self.baseurl, path) + + def send(self, method, path, data=None, headers=None): + url = self._url_builder(path) + data = self.module.jsonify(data) + + resp, info = fetch_url(self.module, url, data=data, headers=self.headers, method=method) + + return Response(resp, info) + + def get(self, path, data=None, headers=None): + return self.send('GET', path, data, headers) + + def put(self, path, data=None, headers=None): + return self.send('PUT', path, data, headers) + + def post(self, path, data=None, headers=None): + return self.send('POST', path, data, headers) + + def patch(self, path, data=None, headers=None): + return self.send('PATCH', path, data, headers) + + def delete(self, path, data=None, headers=None): + return self.send('DELETE', path, data, headers) diff --git a/lib/ansible/modules/network/bigswitch/__init__.py b/lib/ansible/modules/network/bigswitch/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/modules/network/bigswitch/bigmon_chain.py b/lib/ansible/modules/network/bigswitch/bigmon_chain.py new file mode 100755 index 00000000000..a5ec935301a --- /dev/null +++ b/lib/ansible/modules/network/bigswitch/bigmon_chain.py @@ -0,0 +1,159 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Ansible module to manage Big Monitoring Fabric service chains +# (c) 2016, Ted Elhourani , +# +# 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 . + +ANSIBLE_METADATA = {'status': ['preview'], + 'supported_by': 'community', + 'version': '1.0'} + +DOCUMENTATION = ''' +--- +module: bigmon_chain +short_description: Create and remove a bigmon inline service chain. +description: + - Create and remove a bigmon inline service chain. +version_added: "2.3" +options: + name: + description: + - The name of the chain. + required: true + state: + description: + - Whether the service chain should be present or absent. + default: present + choices: ['present', 'absent'] + controller: + description: + - The controller IP address. + required: true + validate_certs: + description: + - If C(false), SSL certificates will not be validated. This should only be used + on personally controlled devices using self-signed certificates. + required: false + default: true + choices: [true, false] + access_token: + description: + - Bigmon access token. + required: false + +notes: + - An environment variable can be used, BIGSWITCH_ACCESS_TOKEN. +''' + + +EXAMPLES = ''' +- name: bigmon inline service chain + bigmon_chain: + name: MyChain + controller: '{{ inventory_hostname }}' + state: present +''' + + +RETURN = ''' +{ + "changed": true, + "invocation": { + "module_args": { + "access_token": null, + "controller": "192.168.86.221", + "name": "MyChain", + "state": "present", + "validate_certs": false + }, + "module_name": "bigmon_chain" + } +} +''' + +import os +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.bigswitch_utils import Rest, Response +from ansible.module_utils.pycompat24 import get_exception + +def chain(module): + try: + access_token = module.params['access_token'] or os.environ['BIGSWITCH_ACCESS_TOKEN'] + except KeyError: + e = get_exception() + module.fail_json(msg='Unable to load %s' % e.message ) + + name = module.params['name'] + state = module.params['state'] + controller = module.params['controller'] + + rest = Rest(module, + {'content-type': 'application/json', 'Cookie': 'session_cookie='+access_token}, + 'https://'+controller+':8443/api/v1/data/controller/applications/bigchain') + + if None in (name, state, controller): + module.fail_json(msg='parameter `name` is missing') + + response = rest.get('chain?config=true', data={}) + if response.status_code != 200: + module.fail_json(msg="failed to obtain existing chain config: {}".format(response.json['description'])) + + config_present = False + matching = [chain for chain in response.json if chain['name'] == name] + if matching: + config_present = True + + if state in ('present') and config_present: + module.exit_json(changed=False) + + if state in ('absent') and not config_present: + module.exit_json(changed=False) + + if state in ('present'): + response = rest.put('chain[name="%s"]' % name, data={'name': name}) + if response.status_code == 204: + module.exit_json(changed=True) + else: + module.fail_json(msg="error creating chain '{}': {}".format(name, response.json['description'])) + + if state in ('absent'): + response = rest.delete('chain[name="%s"]' % name, data={}) + if response.status_code == 204: + module.exit_json(changed=True) + else: + module.fail_json(msg="error deleting chain '{}': {}".format(name, response.json['description'])) + +def main(): + module = AnsibleModule( + argument_spec=dict( + name=dict(type='str', required=True), + controller=dict(type='str', required=True), + state=dict(choices=['present', 'absent'], default='present'), + validate_certs=dict(type='bool', default='False'), + access_token=dict(aliases=['BIGSWITCH_ACCESS_TOKEN'], no_log=True) + ) + ) + + try: + chain(module) + except Exception: + e = get_exception() + module.fail_json(msg=str(e)) + +if __name__ == '__main__': + main()