summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Rupp <caphrim007@gmail.com>2018-01-19 20:57:41 -0800
committerGitHub <noreply@github.com>2018-01-19 20:57:41 -0800
commit90f0c411e4f8bd8437c6a2da6e32b797ce6a32d3 (patch)
treec53ac81c2917cef0f02d5182fd05c899a79c9447
parent585d8cf4c74f51c8d9c086f38cf1903e611f2d2d (diff)
downloadansible-90f0c411e4f8bd8437c6a2da6e32b797ce6a32d3.tar.gz
Adds module for bigip_device_group_member (#35121)
Module allows for members to be managed in a device group
-rw-r--r--lib/ansible/modules/network/f5/bigip_device_group_member.py257
-rw-r--r--test/units/modules/network/f5/test_bigip_device_group_member.py102
2 files changed, 359 insertions, 0 deletions
diff --git a/lib/ansible/modules/network/f5/bigip_device_group_member.py b/lib/ansible/modules/network/f5/bigip_device_group_member.py
new file mode 100644
index 0000000000..b6a1bf912d
--- /dev/null
+++ b/lib/ansible/modules/network/f5/bigip_device_group_member.py
@@ -0,0 +1,257 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 F5 Networks Inc.
+# 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 = r'''
+---
+module: bigip_device_group_member
+short_description: Manages members in a device group
+description:
+ - Manages members in a device group. Members in a device group can only
+ be added or removed, never updated. This is because the members are
+ identified by unique name values and changing that name would invalidate
+ the uniqueness.
+version_added: "2.5"
+options:
+ name:
+ description:
+ - Specifies the name of the device that you want to add to the
+ device group. Often this will be the hostname of the device.
+ This member must be trusted by the device already. Trusting
+ can be done with the C(bigip_device_trust) module and the
+ C(peer_hostname) option to that module.
+ required: True
+ device_group:
+ description:
+ - The device group that you want to add the member to.
+ required: True
+ state:
+ description:
+ - When C(present), ensures that the device group member.
+ - When C(absent), ensures the device group member is removed.
+ default: present
+ choices:
+ - present
+ - absent
+extends_documentation_fragment: f5
+author:
+ - Tim Rupp (@caphrim007)
+'''
+
+EXAMPLES = r'''
+- name: Add the current device to the "device_trust_group" device group
+ bigip_device_group_member:
+ name: "{{ inventory_hostname }}"
+ device_group: device_trust_group
+ password: secret
+ server: lb.mydomain.com
+ state: present
+ user: admin
+ delegate_to: localhost
+
+- name: Add the hosts in the current scope to "device_trust_group"
+ bigip_device_group_member:
+ name: "{{ item }}"
+ device_group: device_trust_group
+ password: secret
+ server: lb.mydomain.com
+ state: present
+ user: admin
+ with_items: "{{ hostvars.keys() }}"
+ run_once: true
+ delegate_to: localhost
+'''
+
+RETURN = r'''
+# only common fields returned
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+HAS_DEVEL_IMPORTS = False
+
+try:
+ # Sideband repository used for dev
+ from library.module_utils.network.f5.bigip import HAS_F5SDK
+ from library.module_utils.network.f5.bigip import F5Client
+ from library.module_utils.network.f5.common import F5ModuleError
+ from library.module_utils.network.f5.common import AnsibleF5Parameters
+ from library.module_utils.network.f5.common import cleanup_tokens
+ from library.module_utils.network.f5.common import fqdn_name
+ from library.module_utils.network.f5.common import f5_argument_spec
+ try:
+ from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
+ except ImportError:
+ HAS_F5SDK = False
+ HAS_DEVEL_IMPORTS = True
+except ImportError:
+ # Upstream Ansible
+ from ansible.module_utils.network.f5.bigip import HAS_F5SDK
+ from ansible.module_utils.network.f5.bigip import F5Client
+ from ansible.module_utils.network.f5.common import F5ModuleError
+ from ansible.module_utils.network.f5.common import AnsibleF5Parameters
+ from ansible.module_utils.network.f5.common import cleanup_tokens
+ from ansible.module_utils.network.f5.common import fqdn_name
+ from ansible.module_utils.network.f5.common import f5_argument_spec
+ try:
+ from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
+ except ImportError:
+ HAS_F5SDK = False
+
+
+class Parameters(AnsibleF5Parameters):
+ api_map = {}
+
+ api_attributes = []
+
+ returnables = []
+
+ updatables = []
+
+ def to_return(self):
+ result = {}
+ try:
+ for returnable in self.returnables:
+ result[returnable] = getattr(self, returnable)
+ result = self._filter_params(result)
+ except Exception:
+ pass
+ return result
+
+
+class Changes(Parameters):
+ pass
+
+
+class ModuleManager(object):
+ def __init__(self, *args, **kwargs):
+ self.module = kwargs.get('module', None)
+ self.client = kwargs.get('client', None)
+ self.want = Parameters(params=self.module.params)
+ self.have = None
+ self.changes = Changes()
+
+ def _set_changed_options(self):
+ changed = {}
+ for key in Parameters.returnables:
+ if getattr(self.want, key) is not None:
+ changed[key] = getattr(self.want, key)
+ if changed:
+ self.changes = Changes(params=changed)
+
+ def exec_module(self):
+ changed = False
+ result = dict()
+ state = self.want.state
+
+ try:
+ if state == "present":
+ changed = self.present()
+ elif state == "absent":
+ changed = self.absent()
+ except iControlUnexpectedHTTPError as e:
+ raise F5ModuleError(str(e))
+
+ changes = self.changes.to_return()
+ result.update(**changes)
+ result.update(dict(changed=changed))
+ return result
+
+ def present(self):
+ if self.exists():
+ return False
+ else:
+ return self.create()
+
+ def exists(self):
+ parent = self.client.api.tm.cm.device_groups.device_group.load(
+ name=self.want.device_group
+ )
+ exists = parent.devices_s.devices.exists(name=self.want.name)
+ if exists:
+ return True
+ return False
+
+ def remove(self):
+ if self.module.check_mode:
+ return True
+ self.remove_from_device()
+ if self.exists():
+ raise F5ModuleError("Failed to remove the member from the device group.")
+ return True
+
+ def create(self):
+ self._set_changed_options()
+ if self.module.check_mode:
+ return True
+ self.create_on_device()
+ return True
+
+ def create_on_device(self):
+ parent = self.client.api.tm.cm.device_groups.device_group.load(
+ name=self.want.device_group
+ )
+ parent.devices_s.devices.create(name=self.want.name)
+
+ def absent(self):
+ if self.exists():
+ return self.remove()
+ return False
+
+ def remove_from_device(self):
+ parent = self.client.api.tm.cm.device_groups.device_group.load(
+ name=self.want.device_group
+ )
+ resource = parent.devices_s.devices.load(name=self.want.name)
+ if resource:
+ resource.delete()
+
+
+class ArgumentSpec(object):
+ def __init__(self):
+ self.supports_check_mode = True
+ argument_spec = dict(
+ name=dict(required=True),
+ device_group=dict(required=True),
+ state=dict(
+ default='present',
+ choices=['absent', 'present']
+ )
+ )
+ self.argument_spec = {}
+ self.argument_spec.update(f5_argument_spec)
+ self.argument_spec.update(argument_spec)
+
+
+def main():
+ spec = ArgumentSpec()
+
+ module = AnsibleModule(
+ argument_spec=spec.argument_spec,
+ supports_check_mode=spec.supports_check_mode
+ )
+ if not HAS_F5SDK:
+ module.fail_json(msg="The python f5-sdk module is required")
+
+ try:
+ client = F5Client(**module.params)
+ mm = ModuleManager(module=module, client=client)
+ results = mm.exec_module()
+ cleanup_tokens(client)
+ module.exit_json(**results)
+ except F5ModuleError as ex:
+ cleanup_tokens(client)
+ module.fail_json(msg=str(ex))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/units/modules/network/f5/test_bigip_device_group_member.py b/test/units/modules/network/f5/test_bigip_device_group_member.py
new file mode 100644
index 0000000000..d83c37274b
--- /dev/null
+++ b/test/units/modules/network/f5/test_bigip_device_group_member.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 F5 Networks Inc.
+# 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
+
+import os
+import json
+import pytest
+import sys
+
+from nose.plugins.skip import SkipTest
+if sys.version_info < (2, 7):
+ raise SkipTest("F5 Ansible modules require Python >= 2.7")
+
+from ansible.compat.tests import unittest
+from ansible.compat.tests.mock import Mock
+from ansible.compat.tests.mock import patch
+from ansible.module_utils.basic import AnsibleModule
+
+try:
+ from library.bigip_device_group_member import Parameters
+ from library.bigip_device_group_member import ModuleManager
+ from library.bigip_device_group_member import ArgumentSpec
+ from library.module_utils.network.f5.common import F5ModuleError
+ from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
+ from test.unit.modules.utils import set_module_args
+except ImportError:
+ try:
+ from ansible.modules.network.f5.bigip_device_group_member import Parameters
+ from ansible.modules.network.f5.bigip_device_group_member import ModuleManager
+ from ansible.modules.network.f5.bigip_device_group_member import ArgumentSpec
+ from ansible.module_utils.network.f5.common import F5ModuleError
+ from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
+ from units.modules.utils import set_module_args
+ except ImportError:
+ raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
+
+fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
+fixture_data = {}
+
+
+def load_fixture(name):
+ path = os.path.join(fixture_path, name)
+
+ if path in fixture_data:
+ return fixture_data[path]
+
+ with open(path) as f:
+ data = f.read()
+
+ try:
+ data = json.loads(data)
+ except Exception:
+ pass
+
+ fixture_data[path] = data
+ return data
+
+
+class TestParameters(unittest.TestCase):
+ def test_module_parameters(self):
+ args = dict(
+ name='bigip1',
+ device_group='dg1'
+ )
+
+ p = Parameters(params=args)
+ assert p.name == 'bigip1'
+ assert p.device_group == 'dg1'
+
+
+class TestManager(unittest.TestCase):
+ def setUp(self):
+ self.spec = ArgumentSpec()
+
+ def test_create(self, *args):
+ set_module_args(
+ dict(
+ name="bigip1",
+ device_group="dg1",
+ state="present",
+ server='localhost',
+ user='admin',
+ password='password'
+ )
+ )
+
+ module = AnsibleModule(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode
+ )
+ mm = ModuleManager(module=module)
+
+ # Override methods to force specific logic in the module to happen
+ mm.create_on_device = Mock(return_value=True)
+ mm.exists = Mock(return_value=False)
+
+ results = mm.exec_module()
+ assert results['changed'] is True