Various fixes for f5 modules (#44734)

A number of bugfixes for the remaining 2.7 work on the F5 modules.
pull/44736/head
Tim Rupp 6 years ago committed by GitHub
parent 0d5390a919
commit 0dacc606b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -207,6 +207,7 @@ from ansible.module_utils.network.common.utils import to_list
from ansible.module_utils.six import string_types
from collections import deque
try:
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
@ -239,6 +240,10 @@ except ImportError:
HAS_CLI_TRANSPORT = False
if HAS_F5SDK:
from f5.sdk_exception import LazyAttributesRequired
class NoChangeReporter(object):
stdout_re = [
# A general error when a resource already exists
@ -635,6 +640,10 @@ class V2Manager(BaseManager):
responses.append(output.strip())
except F5ModuleError:
raise
except LazyAttributesRequired:
# This can happen if there is no "commandResult" attribute in
# the output variable above.
pass
return responses

@ -11127,7 +11127,7 @@ class VirtualServersParameters(BaseParameters):
@property
def enabled(self):
if self._values['enabled'] is None:
return None
return 'no'
elif self._values['enabled'] is True:
return 'yes'
return 'no'

@ -29,6 +29,7 @@ notes:
- Best run as a local_action in your playbook
- Tested with manager and above account privilege level
- C(provision) facts were added in 2.2
- This module is deprecated. Use the C(bigip_device_facts) module instead.
requirements:
- bigsuds
options:
@ -75,7 +76,9 @@ EXAMPLES = r'''
server: lb.mydomain.com
user: admin
password: secret
include: interface,vlan
include:
- interface
- vlan
delegate_to: localhost
'''

@ -374,11 +374,7 @@ class ModuleManager(object):
response = self.client.api.delete(uri)
if response.status == 200:
return True
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(response.content)
raise F5ModuleError(response.content)
def read_current_from_device(self):
uri = "https://{0}:{1}/mgmt/tm/security/firewall/policy/{2}/?expandSubcollections=true".format(

@ -31,8 +31,8 @@ options:
versions it will simply be ignored.
state:
description:
- When C(state) is C(present), ensures that the policy exists.
- When C(state) is C(absent), ensures that the policy is removed.
- When C(state) is C(present), ensures that the rule list exists.
- When C(state) is C(absent), ensures that the rule list is removed.
choices:
- present
- absent

@ -372,6 +372,26 @@ class Parameters(AnsibleF5Parameters):
# This seems to happen only on 12.0.0
elif tmp['value'] == 'none':
tmp['value'] = ''
elif tmp['value'] == 'True':
tmp['value'] = 'yes'
elif tmp['value'] == 'False':
tmp['value'] = 'no'
elif isinstance(tmp['value'], bool):
if tmp['value'] is True:
tmp['value'] = 'yes'
else:
tmp['value'] = 'no'
if tmp['encrypted'] == 'True':
tmp['encrypted'] = 'yes'
elif tmp['encrypted'] == 'False':
tmp['encrypted'] = 'no'
elif isinstance(tmp['encrypted'], bool):
if tmp['encrypted'] is True:
tmp['encrypted'] = 'yes'
else:
tmp['encrypted'] = 'no'
result.append(tmp)
result = sorted(result, key=lambda k: k['name'])
return result
@ -390,6 +410,17 @@ class Parameters(AnsibleF5Parameters):
# BIG-IP removes empty values entries, so mimic this behavior
# for user-supplied values.
tmp['value'] = [str(x) for x in list['value']]
if tmp['encrypted'] == 'True':
tmp['encrypted'] = 'yes'
elif tmp['encrypted'] == 'False':
tmp['encrypted'] = 'no'
elif isinstance(tmp['encrypted'], bool):
if tmp['encrypted'] is True:
tmp['encrypted'] = 'yes'
else:
tmp['encrypted'] = 'no'
result.append(tmp)
result = sorted(result, key=lambda k: k['name'])
return result

@ -85,7 +85,6 @@ RETURN = r'''
'''
import os
import subprocess
import time
from ansible.module_utils.basic import AnsibleModule

@ -419,7 +419,7 @@ class ApiParameters(Parameters):
@property
def state(self):
if self._values['state'] in ['user-up', 'unchecked', 'fqdn-up-no-addr'] and self._values['session'] in ['user-enabled']:
if self._values['state'] in ['user-up', 'unchecked', 'fqdn-up-no-addr', 'fqdn-up'] and self._values['session'] in ['user-enabled']:
return 'present'
elif self._values['state'] in ['down', 'up'] and self._values['session'] == 'monitor-enabled':
return 'present'
@ -476,7 +476,7 @@ class ReportableChanges(Changes):
@property
def state(self):
if self._values['state'] in ['user-up', 'unchecked', 'fqdn-up-no-addr'] and self._values['session'] in ['user-enabled']:
if self._values['state'] in ['user-up', 'unchecked', 'fqdn-up-no-addr', 'fqdn-up'] and self._values['session'] in ['user-enabled']:
return 'present'
elif self._values['state'] in ['down', 'up'] and self._values['session'] == 'monitor-enabled':
return 'present'

@ -592,12 +592,7 @@ class ModuleManager(object):
response = self.client.api.delete(uri)
if response.status == 200:
return True
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(response.content)
raise F5ModuleError(response.content)
def read_current_from_device(self):
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/http/{2}".format(

@ -18,9 +18,8 @@ module: bigip_user
short_description: Manage user accounts and user attributes on a BIG-IP
description:
- Manage user accounts and user attributes on a BIG-IP. Typically this
module operates only on the REST API users and not the CLI users. There
is one exception though and that is if you specify the C(username_credential)
of C(root). When specifying C(root), you may only change the password.
module operates only on the REST API users and not the CLI users.
When specifying C(root), you may only change the password.
Your other parameters will be ignored in this case. Changing the C(root)
password is not an idempotent operation. Therefore, it will change it
every time this module attempts to change it.
@ -31,8 +30,8 @@ options:
- Full name of the user.
username_credential:
description:
- Name of the user to create, remove or modify. There is a special case
that exists for the user C(root).
- Name of the user to create, remove or modify.
- The C(root) user may not be removed.
required: True
aliases:
- name

@ -48,9 +48,23 @@ options:
both ARP and ICMP Echo must be disabled in order for forwarding
virtual servers using that virtual address to forward ICMP packets.
If (enabled), then the packets are dropped.
- Deprecated. Use the C(arp) parameter instead.
- When creating a new virtual address, if this parameter is not specified,
the default value is C(enabled).
choices:
- enabled
- disabled
arp:
description:
- Specifies whether the system accepts ARP requests.
- When C(no), specifies that the system does not accept ARP requests.
- When C(yes), then the packets are dropped.
- Note that both ARP and ICMP Echo must be disabled in order for forwarding
virtual servers using that virtual address to forward ICMP packets.
- When creating a new virtual address, if this parameter is not specified,
the default value is C(yes).
type: bool
version_added: 2.7
auto_delete:
description:
- Specifies whether the system automatically deletes the virtual
@ -58,9 +72,9 @@ options:
When C(disabled), specifies that the system leaves the virtual
address even when all associated virtual servers have been deleted.
When creating the virtual address, the default value is C(enabled).
choices:
- enabled
- disabled
- C(enabled) and C(disabled) are deprecated and will be removed in
Ansible 2.11. Instead, use known Ansible booleans such as C(yes) and
C(no)
icmp_echo:
description:
- Specifies how the systems sends responses to (ICMP) echo requests
@ -157,12 +171,22 @@ options:
- The route domain of the C(address) that you want to use.
- This value cannot be modified after it is set.
version_added: 2.6
notes:
- Requires the netaddr Python package on the host. This is as easy as pip
install netaddr.
spanning:
description:
- Enables all BIG-IP systems in a device group to listen for and process traffic
on the same virtual address.
- Spanning for a virtual address occurs when you enable the C(spanning) option on a
device and then sync the virtual address to the other members of the device group.
- Spanning also relies on the upstream router to distribute application flows to the
BIG-IP systems using ECMP routes. ECMP defines a route to the virtual address using
distinct Floating self-IP addresses configured on each BIG-IP system.
- You must also configure MAC masquerade addresses and disable C(arp) on the virtual
address when Spanning is enabled.
- When creating a new virtual address, if this parameter is not specified, the default
valus is C(no).
version_added: 2.7
type: bool
extends_documentation_fragment: f5
requirements:
- netaddr
author:
- Tim Rupp (@caphrim007)
'''
@ -215,11 +239,11 @@ netmask:
returned: created
type: int
sample: 2345
arp_state:
arp:
description: The new way the virtual address handles ARP requests.
returned: changed
type: string
sample: disabled
type: bool
sample: yes
address:
description: The address of the virtual address.
returned: created
@ -230,6 +254,11 @@ state:
returned: changed
type: string
sample: disabled
spanning:
description: Whether spanning is enabled or not
returned: changed
type: string
sample: disabled
'''
from ansible.module_utils.basic import AnsibleModule
@ -246,6 +275,7 @@ try:
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fq_name
from library.module_utils.network.f5.common import f5_argument_spec
from library.module_utils.network.f5.ipaddress import is_valid_ip
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
@ -258,17 +288,12 @@ except ImportError:
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fq_name
from ansible.module_utils.network.f5.common import f5_argument_spec
from ansible.module_utils.network.f5.ipaddress import is_valid_ip
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
try:
import netaddr
HAS_NETADDR = True
except ImportError:
HAS_NETADDR = False
class Parameters(AnsibleF5Parameters):
api_map = {
@ -278,23 +303,47 @@ class Parameters(AnsibleF5Parameters):
'connectionLimit': 'connection_limit',
'serverScope': 'availability_calculation',
'mask': 'netmask',
'arp': 'arp_state',
'trafficGroup': 'traffic_group',
}
updatables = [
'route_advertisement_type', 'auto_delete', 'icmp_echo', 'connection_limit',
'arp_state', 'enabled', 'availability_calculation', 'traffic_group'
'route_advertisement_type',
'auto_delete',
'icmp_echo',
'connection_limit',
'arp',
'enabled',
'availability_calculation',
'traffic_group',
'spanning',
]
returnables = [
'route_advertisement_type', 'auto_delete', 'icmp_echo', 'connection_limit',
'netmask', 'arp_state', 'address', 'state', 'traffic_group', 'route_domain'
'route_advertisement_type',
'auto_delete',
'icmp_echo',
'connection_limit',
'netmask',
'arp',
'address',
'state',
'traffic_group',
'route_domain',
'spanning',
]
api_attributes = [
'routeAdvertisement', 'autoDelete', 'icmpEcho', 'connectionLimit',
'advertiseRoute', 'arp', 'mask', 'enabled', 'serverScope', 'trafficGroup'
'routeAdvertisement',
'autoDelete',
'icmpEcho',
'connectionLimit',
'advertiseRoute',
'arp',
'mask',
'enabled',
'serverScope',
'trafficGroup',
'spanning',
]
@property
@ -331,10 +380,9 @@ class Parameters(AnsibleF5Parameters):
def netmask(self):
if self._values['netmask'] is None:
return None
try:
ip = netaddr.IPAddress(self._values['netmask'])
return str(ip)
except netaddr.core.AddrFormatError:
if is_valid_ip(self._values['netmask']):
return self._values['netmask']
else:
raise F5ModuleError(
"The provided 'netmask' is not a valid IP address"
)
@ -414,18 +462,41 @@ class Parameters(AnsibleF5Parameters):
class ApiParameters(Parameters):
pass
@property
def arp(self):
if self._values['arp'] is None:
return None
elif self._values['arp'] == 'enabled':
return True
return False
@property
def spanning(self):
if self._values['spanning'] is None:
return None
if self._values['spanning'] == 'enabled':
return True
return False
class ModuleParameters(Parameters):
@property
def arp(self):
if self._values['arp'] is None:
if self.arp_state and self.arp_state == 'enabled':
return True
elif self.arp_state and self.arp_state == 'disabled':
return False
else:
return self._values['arp']
@property
def address(self):
if self._values['address'] is None:
return None
try:
ip = netaddr.IPAddress(self._values['address'])
return str(ip)
except netaddr.core.AddrFormatError:
if is_valid_ip(self._values['address']):
return self._values['address']
else:
raise F5ModuleError(
"The provided 'address' is not a valid IP address"
)
@ -479,9 +550,31 @@ class UsableChanges(Changes):
result = "{0}%{1}".format(self._values['address'], self._values['route_domain'])
return result
@property
def arp(self):
if self._values['arp'] is None:
return None
elif self._values['arp'] is True:
return 'enabled'
elif self._values['arp'] is False:
return 'disabled'
@property
def spanning(self):
if self._values['spanning'] is None:
return None
if self._values['spanning']:
return 'enabled'
return 'disabled'
class ReportableChanges(Changes):
pass
@property
def arp(self):
if self._values['arp'] == 'disabled':
return 'no'
elif self._values['arp'] == 'enabled':
return 'yes'
class Difference(object):
@ -510,6 +603,20 @@ class Difference(object):
if self.want.traffic_group != self.have.traffic_group:
return self.want.traffic_group
@property
def spanning(self):
if self.want.spanning is None:
return None
if self.want.spanning != self.have.spanning:
return self.want.spanning
@property
def arp_state(self):
if self.want.arp_state is None:
return None
if self.want.arp_state != self.have.arp_state:
return self.want.arp_state
class ModuleManager(object):
def __init__(self, *args, **kwargs):
@ -617,6 +724,11 @@ class ModuleManager(object):
"The address cannot be changed. Delete and recreate "
"the virtual address if you need to do this."
)
if self.changes.arp and self.changes.spanning:
raise F5ModuleError(
"'arp' and 'spanning' cannot both be enabled on virtual address."
)
if not self.should_update():
return False
if self.module.check_mode:
@ -636,8 +748,18 @@ class ModuleManager(object):
def create(self):
self._set_changed_options()
if self.want.traffic_group is None:
self.want.update({'traffic_group': '/Common/traffic-group-1'})
if self.want.arp is None:
self.want.update({'arp': True})
if self.want.spanning is None:
self.want.update({'spanning': False})
if self.want.arp and self.want.spanning:
raise F5ModuleError(
"'arp' and 'spanning' cannot both be enabled on virtual address."
)
if self.module.check_mode:
return True
self.create_on_device()
@ -690,12 +812,8 @@ class ArgumentSpec(object):
connection_limit=dict(
type='int'
),
arp_state=dict(
choices=['enabled', 'disabled'],
),
auto_delete=dict(
choices=['enabled', 'disabled'],
),
auto_delete=dict(),
icmp_echo=dict(
choices=['enabled', 'disabled', 'selective'],
),
@ -703,6 +821,15 @@ class ArgumentSpec(object):
choices=['always', 'when_all_available', 'when_any_available'],
aliases=['advertise_route']
),
traffic_group=dict(),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
),
route_domain=dict(),
spanning=dict(type='bool'),
# Deprecated pair - route advertisement
use_route_advertisement=dict(
type='bool',
removed_in_version=2.9,
@ -717,12 +844,13 @@ class ArgumentSpec(object):
'all',
]
),
traffic_group=dict(),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
# Deprecated pair - ARP
arp_state=dict(
choices=['enabled', 'disabled'],
removed_in_version=2.11,
),
route_domain=dict()
arp=dict(type='bool'),
)
self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
@ -731,7 +859,8 @@ class ArgumentSpec(object):
['name', 'address']
]
self.mutually_exclusive = [
['use_route_advertisement', 'route_advertisement']
['use_route_advertisement', 'route_advertisement'],
['arp_state', 'arp']
]
@ -744,8 +873,6 @@ def main():
)
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
if not HAS_NETADDR:
module.fail_json(msg="The python netaddr module is required")
try:
client = F5Client(**module.params)

@ -637,35 +637,31 @@ from ansible.module_utils.six import iteritems
from collections import namedtuple
try:
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.bigip 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 cleanup_tokens
from library.module_utils.network.f5.common import fq_name
from library.module_utils.network.f5.common import f5_argument_spec
from library.module_utils.network.f5.common import fail_json
from library.module_utils.network.f5.common import exit_json
from library.module_utils.network.f5.common import transform_name
from library.module_utils.network.f5.ipaddress import is_valid_ip
from library.module_utils.network.f5.ipaddress import ip_interface
from library.module_utils.network.f5.ipaddress import validate_ip_v6_address
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
except ImportError:
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.bigip 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 cleanup_tokens
from ansible.module_utils.network.f5.common import fq_name
from ansible.module_utils.network.f5.common import f5_argument_spec
from ansible.module_utils.network.f5.common import fail_json
from ansible.module_utils.network.f5.common import exit_json
from ansible.module_utils.network.f5.common import transform_name
from ansible.module_utils.network.f5.ipaddress import is_valid_ip
from ansible.module_utils.network.f5.ipaddress import ip_interface
from ansible.module_utils.network.f5.ipaddress import validate_ip_v6_address
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
class Parameters(AnsibleF5Parameters):
@ -913,20 +909,85 @@ class Parameters(AnsibleF5Parameters):
return False
def _read_current_message_routing_profiles_from_device(self):
collection1 = self.client.api.tm.ltm.profile.diameters.get_collection()
collection2 = self.client.api.tm.ltm.profile.sips.get_collection()
result = [x.name for x in collection1]
result += [x.name for x in collection2]
result = []
result += self._read_diameter_profiles_from_device()
result += self._read_sip_profiles_from_device()
return result
def _read_diameter_profiles_from_device(self):
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/diameter/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [x['name'] for x in response['items']]
return result
def _read_sip_profiles_from_device(self):
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/sip/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [x['name'] for x in response['items']]
return result
def _read_current_fastl4_profiles_from_device(self):
collection = self.client.api.tm.ltm.profile.fastl4s.get_collection()
result = [x.name for x in collection]
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/fastl4/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [x['name'] for x in response['items']]
return result
def _read_current_fasthttp_profiles_from_device(self):
collection = self.client.api.tm.ltm.profile.fasthttps.get_collection()
result = [x.name for x in collection]
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/fasthttp/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [x['name'] for x in response['items']]
return result
@ -1246,6 +1307,12 @@ class ApiParameters(Parameters):
return None
return self._values['security_nat_policy']['policy']
@property
def irules(self):
if self._values['irules'] is None:
return []
return self._values['irules']
class ModuleParameters(Parameters):
services_map = {
@ -2268,25 +2335,104 @@ class VirtualServerValidator(object):
)
def read_dhcp_profiles_from_device(self):
collection = self.client.api.tm.ltm.profile.dhcpv4s.get_collection()
result = [fq_name(self.want.partition, x.name) for x in collection]
collection = self.client.api.tm.ltm.profile.dhcpv6s.get_collection()
result += [fq_name(self.want.partition, x.name) for x in collection]
result = []
result += self.read_dhcpv4_profiles_from_device()
result += self.read_dhcpv6_profiles_from_device()
return result
def read_dhcpv4_profiles_from_device(self):
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/dhcpv4/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [fq_name(self.want.partition, x['name']) for x in response['items']]
return result
def read_dhcpv6_profiles_from_device(self):
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/dhcpv6/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [fq_name(self.want.partition, x['name']) for x in response['items']]
return result
def read_fastl4_profiles_from_device(self):
collection = self.client.api.tm.ltm.profile.fastl4s.get_collection()
result = [fq_name(self.want.partition, x.name) for x in collection]
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/fastl4/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [fq_name(self.want.partition, x['name']) for x in response['items']]
return result
def read_fasthttp_profiles_from_device(self):
collection = self.client.api.tm.ltm.profile.fasthttps.get_collection()
result = [fq_name(self.want.partition, x.name) for x in collection]
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/fasthttp/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [fq_name(self.want.partition, x['name']) for x in response['items']]
return result
def read_udp_profiles_from_device(self):
collection = self.client.api.tm.ltm.profile.udps.get_collection()
result = [fq_name(self.want.partition, x.name) for x in collection]
uri = "https://{0}:{1}/mgmt/tm/ltm/profile/udp/".format(
self.client.provider['server'],
self.client.provider['server_port'],
)
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)
result = [fq_name(self.want.partition, x['name']) for x in response['items']]
return result
@ -2637,13 +2783,10 @@ class ModuleManager(object):
result = dict()
state = self.want.state
try:
if state in ['present', 'enabled', 'disabled']:
changed = self.present()
elif state == "absent":
changed = self.absent()
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e))
if state in ['present', 'enabled', 'disabled']:
changed = self.present()
elif state == "absent":
changed = self.absent()
reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return()
@ -2664,7 +2807,9 @@ class ModuleManager(object):
def update(self):
self.have = self.read_current_from_device()
validator = VirtualServerValidator(module=self.module, client=self.client, have=self.have, want=self.want)
validator = VirtualServerValidator(
module=self.module, client=self.client, have=self.have, want=self.want
)
validator.check_update()
if not self.should_update():
@ -2719,14 +2864,24 @@ class ModuleManager(object):
return False
def exists(self):
result = self.client.api.tm.ltm.virtuals.virtual.exists(
name=self.want.name,
partition=self.want.partition
uri = "https://{0}:{1}/mgmt/tm/ltm/virtual/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
)
return result
resp = self.client.api.get(uri)
try:
response = resp.json()
except ValueError:
return False
if resp.status == 404 or 'code' in response and response['code'] == 404:
return False
return True
def create(self):
validator = VirtualServerValidator(module=self.module, client=self.client, have=self.have, want=self.want)
validator = VirtualServerValidator(
module=self.module, client=self.client, have=self.have, want=self.want
)
validator.check_create()
self._set_changed_options()
@ -2737,42 +2892,73 @@ class ModuleManager(object):
def update_on_device(self):
params = self.changes.api_params()
resource = self.client.api.tm.ltm.virtuals.virtual.load(
name=self.want.name,
partition=self.want.partition
uri = "https://{0}:{1}/mgmt/tm/ltm/virtual/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
)
resource.modify(**params)
resp = self.client.api.patch(uri, json=params)
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)
def read_current_from_device(self):
result = self.client.api.tm.ltm.virtuals.virtual.load(
name=self.want.name,
partition=self.want.partition,
requests_params=dict(
params=dict(
expandSubcollections='true'
)
)
uri = "https://{0}:{1}/mgmt/tm/ltm/virtual/{2}?expandSubcollections=true".format(
self.client.provider['server'],
self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
)
params = result.attrs
params.update(dict(kind=result.to_dict().get('kind', None)))
result = ApiParameters(params=params, client=self.client)
return result
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, client=self.client)
def create_on_device(self):
params = self.changes.api_params()
self.client.api.tm.ltm.virtuals.virtual.create(
name=self.want.name,
partition=self.want.partition,
**params
params['name'] = self.want.name
params['partition'] = self.want.partition
uri = "https://{0}:{1}/mgmt/tm/ltm/virtual/".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, 403]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
def remove_from_device(self):
resource = self.client.api.tm.ltm.virtuals.virtual.load(
name=self.want.name,
partition=self.want.partition
uri = "https://{0}:{1}/mgmt/tm/ltm/virtual/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
)
if resource:
resource.delete()
response = self.client.api.delete(uri)
if response.status == 200:
return True
raise F5ModuleError(response.content)
class ArgumentSpec(object):
@ -2868,18 +3054,16 @@ def main():
supports_check_mode=spec.supports_check_mode,
mutually_exclusive=spec.mutually_exclusive
)
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try:
client = F5Client(**module.params)
client = F5RestClient(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module()
exit_json(module, results, client)
cleanup_tokens(client)
module.exit_json(**results)
except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
fail_json(module, ex, client)
if __name__ == '__main__':

@ -81,25 +81,19 @@ import time
from ansible.module_utils.basic import AnsibleModule
try:
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.bigip 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
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
from library.module_utils.network.f5.common import exit_json
from library.module_utils.network.f5.common import fail_json
except ImportError:
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.bigip 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
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
from ansible.module_utils.network.f5.common import exit_json
from ansible.module_utils.network.f5.common import fail_json
def hard_timeout(module, want, start):
@ -158,10 +152,7 @@ class ModuleManager(object):
def exec_module(self):
result = dict()
try:
changed = self.execute()
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e))
changed = self.execute()
changes = self.changes.to_return()
result.update(**changes)
@ -178,7 +169,7 @@ class ModuleManager(object):
)
def _get_client_connection(self):
return F5Client(**self.module.params)
return F5RestClient(**self.module.params)
def execute(self):
signal.signal(
@ -210,7 +201,7 @@ class ModuleManager(object):
if self._is_mprov_running_on_device():
self._wait_for_module_provisioning()
break
except Exception:
except Exception as ex:
# The types of exception's we're handling here are "REST API is not
# ready" exceptions.
#
@ -248,15 +239,28 @@ class ModuleManager(object):
return False
def _device_is_rebooting(self):
output = self.client.api.tm.util.bash.exec_cmd(
'run',
utilCmdArgs='-c "runlevel"'
params = {
"command": "run",
"utilCmdArgs": '-c "runlevel"'
}
uri = "https://{0}:{1}/mgmt/tm/util/bash".format(
self.client.provider['server'],
self.client.provider['server_port']
)
resp = self.client.api.post(uri, json=params)
try:
if '6' in output.commandResult:
return True
except AttributeError:
return False
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 403]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
if 'commandResult' in response and '6' in response['commandResult']:
return True
return False
def _wait_for_module_provisioning(self):
# To prevent things from running forever, the hack is to check
@ -271,17 +275,32 @@ class ModuleManager(object):
nops += 1
else:
nops = 0
except Exception:
except Exception as ex:
# This can be caused by restjavad restarting.
pass
time.sleep(10)
def _is_mprov_running_on_device(self):
output = self.client.api.tm.util.bash.exec_cmd(
'run',
utilCmdArgs='-c "ps aux | grep \'[m]prov\'"'
params = {
"command": "run",
"utilCmdArgs": '-c "ps aux | grep \'[m]prov\'"'
}
uri = "https://{0}:{1}/mgmt/tm/util/bash".format(
self.client.provider['server'],
self.client.provider['server_port']
)
if hasattr(output, 'commandResult'):
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, 403]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
if 'commandResult' in response:
return True
return False
@ -307,15 +326,14 @@ def main():
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:
mm = ModuleManager(module=module)
client = F5RestClient(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module()
module.exit_json(**results)
except F5ModuleError as e:
module.fail_json(msg=str(e))
exit_json(module, results, client)
except F5ModuleError as ex:
fail_json(module, ex, client)
if __name__ == '__main__':

@ -56,7 +56,7 @@ options:
- This parameter is required when creating a new application.
netmask:
description:
- Specifies the netmask to associate with the given C(destination).
- Specifies the netmask to associate with the given C(address).
- This parameter is required when creating a new application.
port:
description:
@ -80,7 +80,7 @@ options:
- This parameter is required when creating a new application.
netmask:
description:
- Specifies the netmask to associate with the given C(destination).
- Specifies the netmask to associate with the given C(address).
- This parameter is required when creating a new application.
port:
description:
@ -130,8 +130,8 @@ options:
- Passphrases are encrypted on the remote BIG-IP device.
service_environment:
description:
- Specifies the name of service environment or the hostname of the BIG-IP that the application will be
deployed to.
- Specifies the name of service environment or the hostname of the BIG-IP that
the application will be deployed to.
- When creating a new application, this parameter is required.
add_analytics:
description:

@ -20,7 +20,7 @@ from ansible.compat.tests.mock import patch
from ansible.module_utils.basic import AnsibleModule
try:
from library.modules.bigip_virtual_address import Parameters
from library.modules.bigip_virtual_address import ApiParameters
from library.modules.bigip_virtual_address import ModuleParameters
from library.modules.bigip_virtual_address import ModuleManager
from library.modules.bigip_virtual_address import ArgumentSpec
@ -29,7 +29,7 @@ try:
from test.unit.modules.utils import set_module_args
except ImportError:
try:
from ansible.modules.network.f5.bigip_virtual_address import Parameters
from ansible.modules.network.f5.bigip_virtual_address import ApiParameters
from ansible.modules.network.f5.bigip_virtual_address import ModuleParameters
from ansible.modules.network.f5.bigip_virtual_address import ModuleManager
from ansible.modules.network.f5.bigip_virtual_address import ArgumentSpec
@ -79,7 +79,7 @@ class TestParameters(unittest.TestCase):
assert p.address == '1.1.1.1'
assert p.netmask == '2.2.2.2'
assert p.connection_limit == 10
assert p.arp_state == 'enabled'
assert p.arp is True
assert p.auto_delete is True
assert p.icmp_echo == 'enabled'
assert p.availability_calculation == 'none'
@ -87,10 +87,10 @@ class TestParameters(unittest.TestCase):
def test_api_parameters(self):
args = load_fixture('load_ltm_virtual_address_default.json')
p = Parameters(params=args)
p = ApiParameters(params=args)
assert p.name == '1.1.1.1'
assert p.address == '1.1.1.1'
assert p.arp_state == 'enabled'
assert p.arp is True
assert p.auto_delete is True
assert p.connection_limit == 0
assert p.state == 'enabled'
@ -103,42 +103,42 @@ class TestParameters(unittest.TestCase):
args = dict(
availability_calculation='when_all_available'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.availability_calculation == 'all'
def test_module_parameters_advertise_route_any(self):
args = dict(
availability_calculation='when_any_available'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.availability_calculation == 'any'
def test_module_parameters_icmp_echo_disabled(self):
args = dict(
icmp_echo='disabled'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.icmp_echo == 'disabled'
def test_module_parameters_icmp_echo_selective(self):
args = dict(
icmp_echo='selective'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.icmp_echo == 'selective'
def test_module_parameters_auto_delete_disabled(self):
args = dict(
auto_delete='disabled'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.auto_delete is False
def test_module_parameters_arp_state_disabled(self):
args = dict(
arp_state='disabled'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.arp_state == 'disabled'
def test_module_parameters_use_route_advert_disabled(self):
@ -152,7 +152,7 @@ class TestParameters(unittest.TestCase):
args = dict(
state='present'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.state == 'present'
assert p.enabled == 'yes'
@ -160,14 +160,14 @@ class TestParameters(unittest.TestCase):
args = dict(
state='absent'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.state == 'absent'
def test_module_parameters_state_enabled(self):
args = dict(
state='enabled'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.state == 'enabled'
assert p.enabled == 'yes'
@ -175,7 +175,7 @@ class TestParameters(unittest.TestCase):
args = dict(
state='disabled'
)
p = Parameters(params=args)
p = ModuleParameters(params=args)
assert p.state == 'disabled'
assert p.enabled == 'no'

Loading…
Cancel
Save