diff --git a/lib/ansible/modules/network/f5/bigip_monitor_https.py b/lib/ansible/modules/network/f5/bigip_monitor_https.py
new file mode 100644
index 0000000000..b6c27c27b2
--- /dev/null
+++ b/lib/ansible/modules/network/f5/bigip_monitor_https.py
@@ -0,0 +1,604 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2017 F5 Networks Inc.
+#
+# 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',
+ 'metadata_version': '1.1'
+}
+
+DOCUMENTATION = '''
+---
+module: bigip_monitor_https
+short_description: Manages F5 BIG-IP LTM https monitors
+description: Manages F5 BIG-IP LTM https monitors.
+version_added: "2.5"
+options:
+ name:
+ description:
+ - Monitor name.
+ required: True
+ aliases:
+ - monitor
+ parent:
+ description:
+ - The parent template of this monitor template. Once this value has
+ been set, it cannot be changed. By default, this value is the C(https)
+ parent on the C(Common) partition.
+ default: "/Common/https"
+ send:
+ description:
+ - The send string for the monitor call. When creating a new monitor, if
+ this value is not provided, the default C(GET /\\r\\n) will be used.
+ receive:
+ description:
+ - The receive string for the monitor call.
+ receive_disable:
+ description:
+ - This setting works like C(receive), except that the system marks the node
+ or pool member disabled when its response matches the C(receive_disable)
+ string but not C(receive). To use this setting, you must specify both
+ C(receive_disable) and C(receive).
+ ip:
+ description:
+ - IP address part of the IP/port definition. If this parameter is not
+ provided when creating a new monitor, then the default value will be
+ '*'.
+ port:
+ description:
+ - Port address part of the IP/port definition. If this parameter is not
+ provided when creating a new monitor, then the default value will be
+ '*'. Note that if specifying an IP address, a value between 1 and 65535
+ must be specified
+ interval:
+ description:
+ - The interval specifying how frequently the monitor instance of this
+ template will run. If this parameter is not provided when creating
+ a new monitor, then the default value will be 5. This value B(must)
+ be less than the C(timeout) value.
+ timeout:
+ description:
+ - The number of seconds in which the node or service must respond to
+ the monitor request. If the target responds within the set time
+ period, it is considered up. If the target does not respond within
+ the set time period, it is considered down. You can change this
+ number to any number you want, however, it should be 3 times the
+ interval number of seconds plus 1 second. If this parameter is not
+ provided when creating a new monitor, then the default value will be 16.
+ time_until_up:
+ description:
+ - Specifies the amount of time in seconds after the first successful
+ response before a node will be marked up. A value of 0 will cause a
+ node to be marked up immediately after a valid response is received
+ from the node. If this parameter is not provided when creating
+ a new monitor, then the default value will be 0.
+ target_username:
+ description:
+ - Specifies the user name, if the monitored target requires authentication.
+ target_password:
+ description:
+ - Specifies the password, if the monitored target requires authentication.
+ partition:
+ description:
+ - Device partition to manage resources on.
+ required: False
+ default: 'Common'
+notes:
+ - Requires the f5-sdk Python package on the host. This is as easy as pip
+ install f5-sdk.
+ - Requires BIG-IP software version >= 12
+requirements:
+ - f5-sdk >= 2.2.3
+extends_documentation_fragment: f5
+author:
+ - Tim Rupp (@caphrim007)
+'''
+
+EXAMPLES = '''
+- name: Create HTTPS Monitor
+ bigip_monitor_https:
+ state: "present"
+ ip: "10.10.10.10"
+ server: "lb.mydomain.com"
+ user: "admin"
+ password: "secret"
+ name: "my_http_monitor"
+ delegate_to: localhost
+
+- name: Remove HTTPS Monitor
+ bigip_monitor_https:
+ state: "absent"
+ server: "lb.mydomain.com"
+ user: "admin"
+ password: "secret"
+ name: "my_http_monitor"
+ delegate_to: localhost
+'''
+
+RETURN = '''
+parent:
+ description: New parent template of the monitor.
+ returned: changed
+ type: string
+ sample: "https"
+ip:
+ description: The new IP of IP/port definition.
+ returned: changed
+ type: string
+ sample: "10.12.13.14"
+interval:
+ description: The new interval in which to run the monitor check.
+ returned: changed
+ type: int
+ sample: 2
+timeout:
+ description: The new timeout in which the remote system must respond to the monitor.
+ returned: changed
+ type: int
+ sample: 10
+time_until_up:
+ description: The new time in which to mark a system as up after first successful response.
+ returned: changed
+ type: int
+ sample: 2
+'''
+
+import os
+
+try:
+ import netaddr
+ HAS_NETADDR = True
+except ImportError:
+ HAS_NETADDR = False
+
+from ansible.module_utils.f5_utils import AnsibleF5Client
+from ansible.module_utils.f5_utils import AnsibleF5Parameters
+from ansible.module_utils.f5_utils import HAS_F5SDK
+from ansible.module_utils.f5_utils import F5ModuleError
+from ansible.module_utils.f5_utils import iteritems
+from ansible.module_utils.f5_utils import defaultdict
+
+try:
+ from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError
+except ImportError:
+ HAS_F5SDK = False
+
+
+class Parameters(AnsibleF5Parameters):
+ api_map = {
+ 'timeUntilUp': 'time_until_up',
+ 'defaultsFrom': 'parent',
+ 'recv': 'receive'
+ }
+
+ api_attributes = [
+ 'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'recv', 'send',
+ 'destination', 'username', 'password'
+ ]
+
+ returnables = [
+ 'parent', 'send', 'receive', 'ip', 'port', 'interval', 'timeout',
+ 'time_until_up'
+ ]
+
+ updatables = [
+ 'destination', 'send', 'receive', 'interval', 'timeout', 'time_until_up',
+ 'target_username', 'target_password'
+ ]
+
+ def __init__(self, params=None):
+ self._values = defaultdict(lambda: None)
+ self._values['__warnings'] = []
+ if params:
+ self.update(params=params)
+
+ def update(self, params=None):
+ if params:
+ for k, v in iteritems(params):
+ if self.api_map is not None and k in self.api_map:
+ map_key = self.api_map[k]
+ else:
+ map_key = k
+
+ # Handle weird API parameters like `dns.proxy.__iter__` by
+ # using a map provided by the module developer
+ class_attr = getattr(type(self), map_key, None)
+ if isinstance(class_attr, property):
+ # There is a mapped value for the api_map key
+ if class_attr.fset is None:
+ # If the mapped value does not have
+ # an associated setter
+ self._values[map_key] = v
+ else:
+ # The mapped value has a setter
+ setattr(self, map_key, v)
+ else:
+ # If the mapped value is not a @property
+ self._values[map_key] = v
+
+ def to_return(self):
+ result = {}
+ try:
+ for returnable in self.returnables:
+ result[returnable] = getattr(self, returnable)
+ result = self._filter_params(result)
+ return result
+ except Exception:
+ return result
+
+ def api_params(self):
+ result = {}
+ for api_attribute in self.api_attributes:
+ if self.api_map is not None and api_attribute in self.api_map:
+ result[api_attribute] = getattr(self, self.api_map[api_attribute])
+ else:
+ result[api_attribute] = getattr(self, api_attribute)
+ result = self._filter_params(result)
+ return result
+
+ @property
+ def username(self):
+ return self._values['target_username']
+
+ @property
+ def password(self):
+ return self._values['target_password']
+
+ @property
+ def destination(self):
+ if self.ip is None and self.port is None:
+ return None
+ destination = '{0}:{1}'.format(self.ip, self.port)
+ return destination
+
+ @destination.setter
+ def destination(self, value):
+ ip, port = value.split(':')
+ self._values['ip'] = ip
+ self._values['port'] = port
+
+ @property
+ def interval(self):
+ if self._values['interval'] is None:
+ return None
+
+ # Per BZ617284, the BIG-IP UI does not raise a warning about this.
+ # So I do
+ if 1 > int(self._values['interval']) > 86400:
+ raise F5ModuleError(
+ "Interval value must be between 1 and 86400"
+ )
+ return int(self._values['interval'])
+
+ @property
+ def timeout(self):
+ if self._values['timeout'] is None:
+ return None
+ return int(self._values['timeout'])
+
+ @property
+ def ip(self):
+ if self._values['ip'] is None:
+ return None
+ try:
+ if self._values['ip'] in ['*', '0.0.0.0']:
+ return '*'
+ result = str(netaddr.IPAddress(self._values['ip']))
+ return result
+ except netaddr.core.AddrFormatError:
+ raise F5ModuleError(
+ "The provided 'ip' parameter is not an IP address."
+ )
+
+ @property
+ def port(self):
+ if self._values['port'] is None:
+ return None
+ elif self._values['port'] == '*':
+ return '*'
+ return int(self._values['port'])
+
+ @property
+ def time_until_up(self):
+ if self._values['time_until_up'] is None:
+ return None
+ return int(self._values['time_until_up'])
+
+ @property
+ def parent(self):
+ if self._values['parent'] is None:
+ return None
+ if self._values['parent'].startswith('/'):
+ parent = os.path.basename(self._values['parent'])
+ result = '/{0}/{1}'.format(self.partition, parent)
+ else:
+ result = '/{0}/{1}'.format(self.partition, self._values['parent'])
+ return result
+
+ @property
+ def type(self):
+ return 'https'
+
+
+class Difference(object):
+ def __init__(self, want, have=None):
+ self.want = want
+ self.have = have
+
+ def compare(self, param):
+ try:
+ result = getattr(self, param)
+ return result
+ except AttributeError:
+ result = self.__default(param)
+ return result
+
+ @property
+ def parent(self):
+ if self.want.parent != self.want.parent:
+ raise F5ModuleError(
+ "The parent monitor cannot be changed"
+ )
+
+ @property
+ def destination(self):
+ if self.want.ip is None and self.want.port is None:
+ return None
+ if self.want.port is None:
+ self.want.update({'port': self.have.port})
+ if self.want.ip is None:
+ self.want.update({'ip': self.have.ip})
+
+ if self.want.port in [None, '*'] and self.want.ip != '*':
+ raise F5ModuleError(
+ "Specifying an IP address requires that a port number be specified"
+ )
+
+ if self.want.destination != self.have.destination:
+ return self.want.destination
+
+ @property
+ def interval(self):
+ if self.want.timeout is not None and self.want.interval is not None:
+ if self.want.interval >= self.want.timeout:
+ raise F5ModuleError(
+ "Parameter 'interval' must be less than 'timeout'."
+ )
+ elif self.want.timeout is not None:
+ if self.have.interval >= self.want.timeout:
+ raise F5ModuleError(
+ "Parameter 'interval' must be less than 'timeout'."
+ )
+ elif self.want.interval is not None:
+ if self.want.interval >= self.have.timeout:
+ raise F5ModuleError(
+ "Parameter 'interval' must be less than 'timeout'."
+ )
+ if self.want.interval != self.have.interval:
+ return self.want.interval
+
+ def __default(self, param):
+ attr1 = getattr(self.want, param)
+ try:
+ attr2 = getattr(self.have, param)
+ if attr1 != attr2:
+ return attr1
+ except AttributeError:
+ return attr1
+
+
+class ModuleManager(object):
+ def __init__(self, client):
+ self.client = client
+ self.have = None
+ self.want = Parameters(self.client.module.params)
+ self.changes = Parameters()
+
+ 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 = Parameters(changed)
+
+ def _update_changed_options(self):
+ diff = Difference(self.want, self.have)
+ updatables = Parameters.updatables
+ changed = dict()
+ for k in updatables:
+ change = diff.compare(k)
+ if change is None:
+ continue
+ else:
+ changed[k] = change
+ if changed:
+ self.changes = Parameters(changed)
+ return True
+ return False
+
+ def _announce_deprecations(self):
+ warnings = []
+ if self.want:
+ warnings += self.want._values.get('__warnings', [])
+ if self.have:
+ warnings += self.have._values.get('__warnings', [])
+ for warning in warnings:
+ self.client.module.deprecate(
+ msg=warning['msg'],
+ version=warning['version']
+ )
+
+ 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))
+ self._announce_deprecations()
+ return result
+
+ def present(self):
+ if self.exists():
+ return self.update()
+ else:
+ return self.create()
+
+ def create(self):
+ self._set_changed_options()
+ if self.want.timeout is None:
+ self.want.update({'timeout': 16})
+ if self.want.interval is None:
+ self.want.update({'interval': 5})
+ if self.want.time_until_up is None:
+ self.want.update({'time_until_up': 0})
+ if self.want.ip is None:
+ self.want.update({'ip': '*'})
+ if self.want.port is None:
+ self.want.update({'port': '*'})
+ if self.want.send is None:
+ self.want.update({'send': 'GET /\r\n'})
+ if self.client.check_mode:
+ return True
+ self.create_on_device()
+ return True
+
+ def should_update(self):
+ result = self._update_changed_options()
+ if result:
+ return True
+ return False
+
+ def update(self):
+ self.have = self.read_current_from_device()
+ if not self.should_update():
+ return False
+ if self.client.check_mode:
+ return True
+ self.update_on_device()
+ return True
+
+ def absent(self):
+ if self.exists():
+ return self.remove()
+ return False
+
+ def remove(self):
+ if self.client.check_mode:
+ return True
+ self.remove_from_device()
+ if self.exists():
+ raise F5ModuleError("Failed to delete the monitor.")
+ return True
+
+ def read_current_from_device(self):
+ resource = self.client.api.tm.ltm.monitor.https_s.https.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ result = resource.attrs
+ return Parameters(result)
+
+ def exists(self):
+ result = self.client.api.tm.ltm.monitor.https_s.https.exists(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ return result
+
+ def update_on_device(self):
+ params = self.want.api_params()
+ result = self.client.api.tm.ltm.monitor.https_s.https.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ result.modify(**params)
+
+ def create_on_device(self):
+ params = self.want.api_params()
+ self.client.api.tm.ltm.monitor.https_s.https.create(
+ name=self.want.name,
+ partition=self.want.partition,
+ **params
+ )
+
+ def remove_from_device(self):
+ result = self.client.api.tm.ltm.monitor.https_s.https.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ if result:
+ result.delete()
+
+
+class ArgumentSpec(object):
+ def __init__(self):
+ self.supports_check_mode = True
+ self.argument_spec = dict(
+ name=dict(required=True),
+ parent=dict(default='https'),
+ send=dict(),
+ receive=dict(),
+ receive_disable=dict(required=False),
+ ip=dict(),
+ port=dict(type='int'),
+ interval=dict(type='int'),
+ timeout=dict(type='int'),
+ time_until_up=dict(type='int'),
+ target_username=dict(),
+ target_password=dict(no_log=True)
+ )
+ self.f5_product_name = 'bigip'
+
+
+def main():
+ spec = ArgumentSpec()
+
+ client = AnsibleF5Client(
+ argument_spec=spec.argument_spec,
+ supports_check_mode=spec.supports_check_mode,
+ f5_product_name=spec.f5_product_name
+ )
+ try:
+ if not HAS_F5SDK:
+ raise F5ModuleError("The python f5-sdk module is required")
+
+ if not HAS_NETADDR:
+ raise F5ModuleError("The python netaddr module is required")
+
+ mm = ModuleManager(client)
+ results = mm.exec_module()
+ client.module.exit_json(**results)
+ except F5ModuleError as e:
+ client.module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/units/modules/network/f5/fixtures/load_ltm_monitor_https.json b/test/units/modules/network/f5/fixtures/load_ltm_monitor_https.json
new file mode 100644
index 0000000000..ff4f53f277
--- /dev/null
+++ b/test/units/modules/network/f5/fixtures/load_ltm_monitor_https.json
@@ -0,0 +1,30 @@
+{
+ "kind": "tm:ltm:monitor:https:httpsstate",
+ "name": "asdf",
+ "partition": "Common",
+ "fullPath": "/Common/asdf",
+ "generation": 0,
+ "selfLink": "https://localhost/mgmt/tm/ltm/monitor/https/~Common~asdf?ver=13.0.0",
+ "adaptive": "disabled",
+ "adaptiveDivergenceType": "relative",
+ "adaptiveDivergenceValue": 25,
+ "adaptiveLimit": 200,
+ "adaptiveSamplingTimespan": 300,
+ "cipherlist": "DEFAULT:+SHA:+3DES:+kEDH",
+ "compatibility": "enabled",
+ "defaultsFrom": "/Common/https",
+ "description": "this is a description",
+ "destination": "1.1.1.1:389",
+ "interval": 5,
+ "ipDscp": 0,
+ "manualResume": "disabled",
+ "password": "$M$7F$+F0VTCeKM4LbGkpC/u8pwg==",
+ "recv": "hello world",
+ "reverse": "disabled",
+ "send": "GET /\\r\\n",
+ "timeUntilUp": 0,
+ "timeout": 16,
+ "transparent": "disabled",
+ "upInterval": 0,
+ "username": "john"
+}
diff --git a/test/units/modules/network/f5/test_bigip_monitor_https.py b/test/units/modules/network/f5/test_bigip_monitor_https.py
new file mode 100644
index 0000000000..68144dae68
--- /dev/null
+++ b/test/units/modules/network/f5/test_bigip_monitor_https.py
@@ -0,0 +1,457 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2017 F5 Networks Inc.
+#
+# 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 Liccense for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import os
+import json
+import sys
+import pytest
+
+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 patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible.module_utils.f5_utils import AnsibleF5Client
+from ansible.module_utils.f5_utils import F5ModuleError
+
+try:
+ from library.bigip_monitor_https import Parameters
+ from library.bigip_monitor_https import ModuleManager
+ from library.bigip_monitor_https import ArgumentSpec
+ from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError
+except ImportError:
+ try:
+ from ansible.modules.network.f5.bigip_monitor_https import Parameters
+ from ansible.modules.network.f5.bigip_monitor_https import ModuleManager
+ from ansible.modules.network.f5.bigip_monitor_https import ArgumentSpec
+ from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError
+ 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 set_module_args(args):
+ args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
+ basic._ANSIBLE_ARGS = to_bytes(args)
+
+
+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='foo',
+ parent='parent',
+ send='this is a send string',
+ receive='this is a receive string',
+ ip='10.10.10.10',
+ port=80,
+ interval=20,
+ timeout=30,
+ time_until_up=60,
+ partition='Common'
+ )
+
+ p = Parameters(args)
+ assert p.name == 'foo'
+ assert p.parent == '/Common/parent'
+ assert p.send == 'this is a send string'
+ assert p.receive == 'this is a receive string'
+ assert p.ip == '10.10.10.10'
+ assert p.type == 'https'
+ assert p.port == 80
+ assert p.destination == '10.10.10.10:80'
+ assert p.interval == 20
+ assert p.timeout == 30
+ assert p.time_until_up == 60
+
+ def test_module_parameters_ints_as_strings(self):
+ args = dict(
+ name='foo',
+ parent='parent',
+ send='this is a send string',
+ receive='this is a receive string',
+ ip='10.10.10.10',
+ port='80',
+ interval='20',
+ timeout='30',
+ time_until_up='60',
+ partition='Common'
+ )
+
+ p = Parameters(args)
+ assert p.name == 'foo'
+ assert p.parent == '/Common/parent'
+ assert p.send == 'this is a send string'
+ assert p.receive == 'this is a receive string'
+ assert p.ip == '10.10.10.10'
+ assert p.type == 'https'
+ assert p.port == 80
+ assert p.destination == '10.10.10.10:80'
+ assert p.interval == 20
+ assert p.timeout == 30
+ assert p.time_until_up == 60
+
+ def test_api_parameters(self):
+ args = dict(
+ name='foo',
+ defaultsFrom='/Common/parent',
+ send='this is a send string',
+ recv='this is a receive string',
+ destination='10.10.10.10:80',
+ interval=20,
+ timeout=30,
+ timeUntilUp=60
+ )
+
+ p = Parameters(args)
+ assert p.name == 'foo'
+ assert p.parent == '/Common/parent'
+ assert p.send == 'this is a send string'
+ assert p.receive == 'this is a receive string'
+ assert p.ip == '10.10.10.10'
+ assert p.type == 'https'
+ assert p.port == 80
+ assert p.destination == '10.10.10.10:80'
+ assert p.interval == 20
+ assert p.timeout == 30
+ assert p.time_until_up == 60
+
+
+@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
+ return_value=True)
+class TestManager(unittest.TestCase):
+
+ def setUp(self):
+ self.spec = ArgumentSpec()
+
+ def test_create_monitor(self, *args):
+ set_module_args(dict(
+ name='foo',
+ parent='parent',
+ send='this is a send string',
+ receive='this is a receive string',
+ ip='10.10.10.10',
+ port=80,
+ interval=20,
+ timeout=30,
+ time_until_up=60,
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(side_effect=[False, True])
+ mm.create_on_device = Mock(return_value=True)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['parent'] == '/Common/parent'
+
+ def test_create_monitor_idempotent(self, *args):
+ set_module_args(dict(
+ name='asdf',
+ parent='https',
+ send='GET /\\r\\n',
+ receive='hello world',
+ ip='1.1.1.1',
+ port=389,
+ interval=5,
+ timeout=16,
+ time_until_up=0,
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is False
+
+ def test_update_port(self, *args):
+ set_module_args(dict(
+ name='asdf',
+ port=800,
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+ mm.update_on_device = Mock(return_value=True)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['port'] == 800
+
+ def test_update_interval(self, *args):
+ set_module_args(dict(
+ name='foo',
+ interval=10,
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+ mm.update_on_device = Mock(return_value=True)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['interval'] == 10
+
+ def test_update_interval_larger_than_existing_timeout(self, *args):
+ set_module_args(dict(
+ name='asdf',
+ interval=30,
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+ mm.update_on_device = Mock(return_value=True)
+
+ with pytest.raises(F5ModuleError) as ex:
+ mm.exec_module()
+
+ assert "must be less than" in str(ex)
+
+ def test_update_interval_larger_than_new_timeout(self, *args):
+ set_module_args(dict(
+ name='asdf',
+ interval=10,
+ timeout=5,
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+ mm.update_on_device = Mock(return_value=True)
+
+ with pytest.raises(F5ModuleError) as ex:
+ mm.exec_module()
+
+ assert "must be less than" in str(ex)
+
+ def test_update_send(self, *args):
+ set_module_args(dict(
+ name='asdf',
+ send='this is another send string',
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+ mm.update_on_device = Mock(return_value=True)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['send'] == 'this is another send string'
+
+ def test_update_receive(self, *args):
+ set_module_args(dict(
+ name='asdf',
+ receive='this is another receive string',
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+ mm.update_on_device = Mock(return_value=True)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['receive'] == 'this is another receive string'
+
+ def test_update_timeout(self, *args):
+ set_module_args(dict(
+ name='asdf',
+ timeout=300,
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+ mm.update_on_device = Mock(return_value=True)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['timeout'] == 300
+
+ def test_update_time_until_up(self, *args):
+ set_module_args(dict(
+ name='asdf',
+ time_until_up=300,
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ current = Parameters(load_fixture('load_ltm_monitor_https.json'))
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name
+ )
+
+ # Override methods in the specific type of manager
+ mm = ModuleManager(client)
+ mm.exists = Mock(return_value=True)
+ mm.read_current_from_device = Mock(return_value=current)
+ mm.update_on_device = Mock(return_value=True)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['time_until_up'] == 300