community.general/lib/ansible/modules/network/f5/bigiq_device_discovery.py

1240 lines
38 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: (c) 2019, 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': 'certified'}
DOCUMENTATION = r'''
---
module: bigiq_device_discovery
short_description: Manage BIG-IP devices through BIG-IQ
description:
- Discovers and imports BIG-IP device configuration on the BIG-IQ.
version_added: 2.8
options:
device_address:
description:
- The IP address of the BIG-IP device to be imported/managed.
type: str
required: True
device_username:
description:
- The administrator username for the BIG-IP device.
- This parameter is only required when adding a new BIG-IP device to be managed.
type: str
device_password:
description:
- The administrator password for the BIG-IP device.
- This parameter is only required when adding a new BIG-IP device to be managed.
type: str
device_port:
description:
- The port on which a device trust setup between BIG-IQ and BIG-IP should happen.
type: int
default: 443
ha_name:
description:
- DSC cluster name of the BIG-IP device to be managed.
- This is optional if the managed device is not a part of a cluster group.
- When C(use_bigiq_sync) is set to C(yes) then this parameter becomes mandatory.
type: str
use_bigiq_sync:
description:
- When set to true, BIG-IQ will manually synchronize configuration changes
between members in a DSC cluster.
type: bool
default: no
conflict_policy:
description:
- Sets the conflict resolution policy for shared objects across BIG-IP devices, except LTM profiles and monitors.
type: str
choices:
- use_bigiq
- use_bigip
default: use_bigiq
versioned_conflict_policy:
description:
- Sets the conflict resolution policy for LTM profile and monitor objects that are specific to a BIG-IP software
version.
type: str
choices:
- use_bigiq
- use_bigip
- keep_version
device_conflict_policy:
description:
- Sets the conflict resolution policy for objects that are specific to a particular to a BIG-IP device
and not shared among BIG-IP devices.
type: str
choices:
- use_bigiq
- use_bigip
default: use_bigiq
access_conflict_policy:
description:
- Sets the conflict resolution policy for Access module C(apm) objects, only used when C(apm) module is specified.
type: str
choices:
- use_bigiq
- use_bigip
- keep_version
access_group_name:
description:
- Access group name to import Access configuration for devices, once set it cannot be changed.
type: str
access_group_first_device:
description:
- Specifies if the imported device is the first device in the access group to import shared configuration for that
access group.
type: bool
default: yes
force:
description:
- Forces rediscovery and import of existing modules on the managed BIG-IP
type: bool
default: no
modules:
description:
- List of modules to be discovered and imported into the device.
- These modules must be provisioned on the target device otherwise operation will fail.
- The C(ltm) module must always be specified when performing discovery or re-discovery of the the device.
- When C(asm) or C(afm) are specified C(shared_security) module needs to also be declared.
type: list
choices:
- ltm
- asm
- apm
- afm
- dns
- websafe
- security_shared
statistics:
description:
- Specify the statistics collection for discovered device.
suboptions:
enable:
description:
- Enables statistics collection on a device
type: bool
default: no
interval:
description:
- Specify the interval in seconds the data is collected from the discovered device.
type: int
default: 60
choices:
- 30
- 60
- 120
- 500
zone:
description:
- Specify in which DCD zone is collecting the data from device.
type: str
default: default
stat_modules:
description:
- Specifies for which modules the data is being collected.
type: list
default: ['device', 'ltm']
choices:
- device
- ltm
- dns
state:
description:
- The state of the managed device on the system.
- When C(present), enables new device addition as well as device rediscovery/import.
- When C(absent), completely removes the device from the system.
type: str
choices:
- absent
- present
default: present
extends_documentation_fragment: f5
notes:
- BIG-IQ >= 6.1.0.
- This module does not support atomic removal of discovered modules on the device.
author:
- Wojciech Wypior (@wojtek0806)
'''
EXAMPLES = r'''
- name: Discover a new device and import config, use default conflict policy.
bigiq_device_discovery:
device_address: 192.168.1.1
device_username: bigipadmin
device_password: bigipsecret
modules:
- ltm
- afm
- shared_security
provider:
password: secret
server: lb.mydomain.com
user: admin
delegate_to: localhost
- name: Discover a new device and import config, use non- default conflict policy.
bigiq_device_discovery:
device_address: 192.168.1.1
modules:
- ltm
- dns
conflict_policy: use_bigip
provider:
password: secret
server: lb.mydomain.com
user: admin
delegate_to: localhost
- name: Force full device rediscovery
bigiq_device_discovery:
device_address: 192.168.1.1
modules:
- ltm
- afm
- dns
- shared_security
force: yes
provider:
password: secret
server: lb.mydomain.com
user: admin
delegate_to: localhost
- name: Remove discovered device and its config
bigiq_device_discovery:
device_address: 192.168.1.1
state: absent
provider:
password: secret
server: lb.mydomain.com
user: admin
delegate_to: localhost
'''
RETURN = r'''
device_address:
description: The IP address of the BIG-IP device to be imported/managed.
returned: changed
type: str
sample: 192.168.1.1
device_port:
description: The port on which a device trust setup between BIG-IQ and BIG-IP should happen.
returned: changed
type: int
sample: 10443
ha_name:
description: DSC cluster name of the BIG-IP device to be managed.
returned: changed
type: str
sample: GROUP_1
use_bigiq_sync:
description: Indicate if BIG-IQ should manually synchronise DSC configuration.
returned: changed
type: bool
sample: yes
conflict_policy:
description: Sets the conflict resolution policy for shared objects across BIG-IP devices.
returned: changed
type: str
sample: use_bigip
device_conflict_policy:
description: Sets the conflict resolution policy for objects that are specific to a particular to a BIG-IP device.
returned: changed
type: str
sample: use_bigip
versioned_conflict_policy:
description: Sets the conflict resolution policy for LTM profile and monitor objects.
returned: changed
type: str
sample: keep_version
access_conflict_policy:
description: Sets the conflict resolution policy for Access module C(apm) objects.
returned: changed
type: str
sample: keep_version
access_group_name:
description: Access group name to import Access configuration for devices.
returned: changed
type: str
sample: foo_group
access_group_first_device:
description: First device in the access group to import shared configuration for that access group.
returned: changed
type: bool
sample: yes
modules:
description: List of modules to be discovered and imported into the device.
returned: changed
type: list
sample: ['ltm', 'dns']
'''
import time
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule
try:
from library.module_utils.network.f5.bigiq import F5RestClient
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 f5_argument_spec
from library.module_utils.network.f5.common import flatten_boolean
from library.module_utils.network.f5.ipaddress import is_valid_ip
from library.module_utils.network.f5.icontrol import bigiq_version
except ImportError:
from ansible.module_utils.network.f5.bigiq import F5RestClient
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 f5_argument_spec
from ansible.module_utils.network.f5.common import flatten_boolean
from ansible.module_utils.network.f5.ipaddress import is_valid_ip
from ansible.module_utils.network.f5.icontrol import bigiq_version
class Parameters(AnsibleF5Parameters):
api_map = {
'address': 'device_address',
'userName': 'device_username',
'password': 'device_password',
'httpsPort': 'device_port',
'clusterName': 'ha_name',
'useBigiqSync': 'use_bigiq_sync',
}
api_attributes = [
'address',
'userName',
'password',
'httpsPort',
'clusterName',
'useBigiqSync',
]
returnables = [
'device_address',
'device_username',
'device_password',
'device_port',
'ha_name',
'use_bigiq_sync',
'modules',
'conflict_policy',
'versioned_conflict_policy',
'device_conflict_policy',
'access_group_name',
'access_group_first_device',
'access_conflict_policy',
'module_list',
'apm_properties',
]
updatables = [
'modules',
'access_group_name',
'apm_properties',
'module_list',
]
class ApiParameters(Parameters):
module_map = {
'cm-security-shared-allSharedDevices': 'security_shared',
'cm-asm-allAsmDevices': 'asm',
'cm-firewall-allFirewallDevices': 'firewall',
'cm-websafe-allFpsDevices': 'fps',
'cm-dns-allBigIpDevices': 'dns',
'cm-adccore-allbigipDevices': 'adc_core',
'cm-access-allBigIpDevices': 'access',
}
@property
def modules(self):
raw_data = self._values['properties']
if raw_data is None:
return None
result = list()
for item in raw_data.keys():
if item in self.module_map:
if raw_data[item]['discovered'] is True and raw_data[item]['imported'] is True:
result.append(self.module_map[item])
return result
@property
def access_group_name(self):
raw_data = self._values['properties']
if raw_data is None:
return None
for item in raw_data.keys():
if 'cm:access:access-group-name' in raw_data[item]:
return raw_data[item]['cm:access:access-group-name']
return None
class ModuleParameters(Parameters):
module_map = {
'ltm': 'adc_core',
'afm': 'firewall',
'websafe': 'fps',
'apm': 'access',
}
@property
def device_password(self):
if self._values['device_password'] is None:
return None
return self._values['device_password']
@property
def device_username(self):
if self._values['device_username'] is None:
return None
return self._values['device_username']
@property
def device_address(self):
if is_valid_ip(self._values['device_address']):
return self._values['device_address']
raise F5ModuleError(
'Provided device address: {0} is not a valid IP.'.format(self._values['device_address'])
)
@property
def device_port(self):
if self._values['device_port'] is None:
return None
return int(self._values['device_port'])
@property
def conflict_policy(self):
return self._values['conflict_policy'].upper()
@property
def device_conflict_policy(self):
return self._values['device_conflict_policy'].upper()
@property
def versioned_conflict_policy(self):
if self._values['versioned_conflict_policy'] is None:
return None
return self._values['versioned_conflict_policy'].upper()
@property
def access_conflict_policy(self):
if self._values['access_conflict_policy'] is None:
return None
return self._values['device_conflict_policy'].upper()
@property
def modules(self):
if self._values['modules'] is None:
return None
result = list()
if 'security_shared' not in self._values['modules']:
if 'afm' in self._values['modules']:
raise F5ModuleError(
"Module 'shared_security' required for 'afm' module."
)
if 'asm' in self._values['modules']:
raise F5ModuleError(
"Module 'shared_security' required for 'asm' module."
)
if 'ltm' not in self._values['modules']:
raise F5ModuleError(
"LTM module must be specified for device discovery and import."
)
if 'apm' in self._values['modules']:
if not self.access_group_name or not self.access_conflict_policy:
raise F5ModuleError(
"When importing APM 'access_group_name' and 'access_conflict_policy' must be specified."
)
for item in self._values['modules']:
if item in self.module_map:
result.append(self.module_map[item])
else:
result.append(item)
return result
@property
def apm_properties(self):
if self._values['modules'] is None:
return None
if 'apm' in self._values['modules']:
result = {
'cm:access:conflict-resolution': self.access_conflict_policy,
'cm:access:access-group-name': self.access_group_name,
'cm:access:import-shared': self.access_group_first_device
}
return result
@property
def use_bigiq_sync(self):
result = flatten_boolean(self._values['use_bigiq_sync'])
if result:
if result == 'yes':
return True
return False
@property
def access_group_first_device(self):
result = flatten_boolean(self._values['access_group_first_device'])
if result:
if result == 'yes':
return True
return False
@property
def stats_enabled(self):
if self._values['statistics'] is None:
return None
result = flatten_boolean(self._values['statistics']['enable'])
if result:
if result == 'yes':
return True
return False
@property
def interval(self):
if self._values['statistics'] is None:
return None
return self._values['statistics']['interval']
@property
def zone(self):
if self._values['statistics'] is None:
return None
return self._values['statistics']['zone']
@property
def stat_modules(self):
if self._values['statistics'] is None:
return None
modules = self._values['statistics']['stat_modules']
result = list()
for module in modules:
result.append((dict(module=module.upper())))
return result
class Changes(Parameters):
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 UsableChanges(Changes):
@property
def modules(self):
if self._values['modules'] is None:
return None
result = list()
for item in self._values['modules']:
result.append(dict(module=item))
return result
@property
def module_list(self):
if self._values['modules'] is None:
return None
result = list()
for item in self._values['modules']:
if item == 'access':
result.append(dict(module=item, properties=self._values['apm_properties']))
else:
result.append(dict(module=item))
return result
class ReportableChanges(Changes):
@property
def module_list(self):
return None
@property
def apm_properties(self):
return None
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:
return self.__default(param)
def __default(self, param):
attr1 = getattr(self.want, param)
try:
attr2 = getattr(self.have, param)
if attr1 != attr2:
return attr1
except AttributeError:
return attr1
@property
def modules(self):
if self.want.modules is None:
return None
if self.have.modules is None:
return self.want.modules
if set(self.want.modules).issubset(self.have.modules):
return None
if set(self.want.modules) != set(self.have.modules):
return self.want.modules
@property
def access_group_name(self):
if self.want.access_group_name != self.have.access_group_name:
raise F5ModuleError(
'Access group name cannot be modified once it is set.'
)
@property
def apm_properties(self):
# This is required for idempotency and updates as we do not compare these properties
return None
class ModuleManager(object):
def __init__(self, *args, **kwargs):
self.module = kwargs.get('module', None)
self.client = F5RestClient(**self.module.params)
self.want = ModuleParameters(params=self.module.params)
self.have = ApiParameters()
self.changes = UsableChanges()
self.device_id = None
self.task_id = None
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 = UsableChanges(params=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:
if isinstance(change, dict):
changed.update(change)
else:
changed[k] = change
if changed:
changed['apm_properties'] = self.want.apm_properties
self.changes = UsableChanges(params=changed)
return True
return False
def _announce_deprecations(self, result):
warnings = result.pop('__warnings', [])
for warning in warnings:
self.client.module.deprecate(
msg=warning['msg'],
version=warning['version']
)
def check_bigiq_version(self):
version = bigiq_version(self.client)
if LooseVersion(version) < LooseVersion('6.1.0'):
raise F5ModuleError(
'Module supports only BIGIQ version 6.1.x or higher.'
)
def exec_module(self):
self.check_bigiq_version()
changed = False
result = dict()
state = self.want.state
if state == "present":
changed = self.present()
elif state == "absent":
changed = self.absent()
reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return()
result.update(**changes)
result.update(dict(changed=changed))
self._announce_deprecations(result)
return result
def present(self):
if self.exists():
return self.update()
else:
return self.create()
def absent(self):
if self.exists():
return self.remove()
return False
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() and not self.want.force:
return False
if self.module.check_mode:
return True
if self.want.force:
self._set_changed_options()
self.discover_on_device()
self.import_modules_on_device()
if self.want.stats_enabled:
self.enable_stats_on_device()
return True
def remove(self):
if self.module.check_mode:
return True
self.remove_autority_from_device()
self.remove_trust_from_device()
return True
def create(self):
if self.want.modules is None:
raise F5ModuleError(
'List of modules cannot be empty if discovering a device.'
)
self._set_changed_options()
if self.module.check_mode:
return True
self.set_trust_with_device()
self.discover_on_device()
self.import_modules_on_device()
if self.want.stats_enabled:
self.enable_stats_on_device()
return True
def exists(self):
uri = "https://{0}:{1}/mgmt/cm/system/machineid-resolver".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
query = "?$filter=address eq '{0}'".format(self.want.device_address)
resp = self.client.api.get(uri + query)
try:
response = resp.json()
except ValueError:
return False
if resp.status == 404 or 'code' in response and response['code'] == 404:
raise F5ModuleError(response.message)
if 'items' in response:
if not response['items']:
return False
self.device_id = response['items'][0]['machineId']
return True
return False
def set_trust_with_device(self):
params = self.changes.api_params()
params['name'] = 'trust_{0}'.format(self.want.device_address)
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-trust/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 409]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
task = "https://{0}:{1}/mgmt/cm/global/tasks/device-trust/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
response['id']
)
query = "?$select=status,currentStep,errorMessage"
if self._wait_for_task(task + query):
self._set_device_id(task)
return True
def _set_device_id(self, uri):
resp = self.client.api.get(uri)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
self.device_id = response['machineId']
def _wait_for_task(self, uri):
while True:
resp = self.client.api.get(uri)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
if response['status'] in ['FINISHED', 'FAILED', 'CANCELLED']:
break
time.sleep(1)
if response['status'] == 'FAILED':
raise F5ModuleError(response['errorMessage'])
if response['status'] == 'CANCELLED':
raise F5ModuleError(
'The task process has been cancelled.'
)
if response['status'] == 'FINISHED':
return True
def discover_on_device(self):
tmp = self.changes.to_return()
if self.reuse_task_on_device('discovery'):
params = dict(
moduleList=tmp['modules'],
status='STARTED'
)
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-discovery/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
self.task_id
)
resp = self.client.api.patch(uri, json=params)
else:
params = dict(
name='discovery_{0}'.format(self.want.device_address),
moduleList=tmp['modules'],
deviceReference=dict(link='https://localhost/mgmt/cm/system/machineid-resolver/{0}'.format(
self.device_id
)
),
status='STARTED'
)
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-discovery".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 409]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
task = "https://{0}:{1}/mgmt/cm/global/tasks/device-discovery/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
response['id']
)
query = "?$select=status,currentStep,errorMessage"
self._wait_for_task(task + query)
return True
def import_modules_on_device(self):
tmp = self.changes.to_return()
if self.reuse_task_on_device('import'):
params = dict(
moduleList=tmp['module_list'],
conflictPolicy=self.want.conflict_policy,
deviceConflictPolicy=self.want.device_conflict_policy,
status='STARTED'
)
if self.want.versioned_conflict_policy:
params['versionedConflictPolicy'] = self.want.versioned_conflict_policy
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-import/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
self.task_id
)
resp = self.client.api.patch(uri, json=params)
else:
params = dict(
name='import_{0}'.format(self.want.device_address),
moduleList=tmp['module_list'],
conflictPolicy=self.want.conflict_policy,
deviceConflictPolicy=self.want.device_conflict_policy,
deviceReference=dict(link='https://localhost/mgmt/cm/system/machineid-resolver/{0}'.format(
self.device_id
)
),
status='STARTED'
)
if self.want.versioned_conflict_policy:
params['versionedConflictPolicy'] = self.want.versioned_conflict_policy
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-import".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 409]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
task = "https://{0}:{1}/mgmt/cm/global/tasks/device-import/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
response['id']
)
query = "?$select=status,currentStep,errorMessage"
self._wait_for_task(task + query)
return True
def enable_stats_on_device(self):
params = dict(
enabled=self.want.stats_enabled,
pushIntervalSecs=self.want.interval,
zone=self.want.zone,
modules=self.want.stat_modules,
targetDeviceReference=dict(
link='https://localhost/mgmt/cm/system/machineid-resolver/{0}'.format(
self.device_id
)
),
)
uri = "https://{0}:{1}/mgmt/cm/shared/stats-mgmt/agent-install-and-config-task".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 409]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
task = "https://{0}:{1}/mgmt/cm/shared/stats-mgmt/agent-install-and-config-task/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
response['id']
)
query = "?$select=status,currentStep,errorMessage"
self._wait_for_task(task + query)
return True
def reuse_task_on_device(self, task):
if task == 'discovery':
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-discovery".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
else:
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-import".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
query = "?$filter=deviceReference/link eq '{0}'".format(self.device_id)
resp = self.client.api.get(uri + query)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 409]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
if 'items' in response:
if response['items']:
self.task_id = response['id']
return True
return False
def remove_autority_from_device(self):
# We can provide all of the modules for removal task, without ensuring they were discovered
modules = [
{'module': 'adc_core'},
{'module': 'access'},
{'module': 'asm'},
{'module': 'fps'},
{'module': 'firewall'},
{'module': 'security_shared'},
{'module': 'dns'}
]
params = dict(
moduleList=modules,
deviceReference=dict(
link="https://localhost/mgmt/cm/system/machineid-resolver/{0}".format(self.device_id)
),
name='remove_auth_{0}'.format(self.want.device_address)
)
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-remove-mgmt-authority/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 409]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
task = "https://{0}:{1}/mgmt/cm/global/tasks/device-remove-mgmt-authority/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
response['id']
)
query = "?$select=status,currentStep,errorMessage"
self._wait_for_task(task + query)
def remove_trust_from_device(self):
params = dict(
deviceReference=dict(
link="https://localhost/mgmt/cm/system/machineid-resolver/{0}".format(self.device_id)
),
name='remove_auth_{0}'.format(self.want.device_address)
)
uri = "https://{0}:{1}/mgmt/cm/global/tasks/device-remove-trust/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 409]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
task = "https://{0}:{1}/mgmt/cm/global/tasks/device-remove-trust/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
response['id']
)
query = "?$select=status,currentStep,errorMessage"
self._wait_for_task(task + query)
def read_current_from_device(self):
uri = "https://{0}:{1}/mgmt/cm/system/machineid-resolver/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
self.device_id
)
resp = self.client.api.get(uri)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
return ApiParameters(params=response)
class ArgumentSpec(object):
def __init__(self):
self.supports_check_mode = True
self.conflict = ['use_bigip', 'use_bigiq']
argument_spec = dict(
device_address=dict(
required=True
),
device_username=dict(
no_log=True
),
device_password=dict(
no_log=True
),
device_port=dict(
type='int',
default=443
),
ha_name=dict(),
use_bigiq_sync=dict(
type='bool',
default='no'
),
conflict_policy=dict(
choices=self.conflict,
default='use_bigiq'
),
versioned_conflict_policy=dict(
choices=self.conflict + ['keep_version'],
),
device_conflict_policy=dict(
choices=self.conflict,
default='use_bigiq'
),
force=dict(
type='bool',
default='no'
),
modules=dict(
type='list',
choices=[
'ltm', 'asm', 'afm', 'dns', 'websafe', 'security_shared', 'apm'
]
),
access_conflict_policy=dict(
choices=self.conflict + ['keep_version']
),
access_group_name=dict(),
access_group_first_device=dict(
type='bool',
default='yes'
),
statistics=dict(
type='dict',
options=dict(
enable=dict(
type='bool',
default='no'
),
interval=dict(
type='int',
choices=[
30, 60, 120, 500
],
default=60
),
zone=dict(
type='str',
default='default'
),
stat_modules=dict(
type='list',
choices=[
'device', 'ltm', 'dns'
],
default=[
'device', 'ltm'
]
)
)
),
state=dict(default='present', choices=['absent', 'present']),
)
self.required_if = [
['use_bigiq_sync', True, ['ha_name']]
]
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,
required_if=spec.required_if
)
try:
mm = ModuleManager(module=module)
results = mm.exec_module()
module.exit_json(**results)
except F5ModuleError as ex:
module.fail_json(msg=str(ex))
if __name__ == '__main__':
main()