More bug fixes before 2.5 (#35260)

This patch includes more fixes for bugs that were identified in
the upcoming 2.5 release.
pull/35298/head
Tim Rupp 7 years ago committed by GitHub
parent c643a5d628
commit bd09e67438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -84,6 +84,7 @@ f5_top_spec = {
), ),
'transport': dict( 'transport': dict(
removed_in_version=2.9, removed_in_version=2.9,
default='rest',
choices=['cli', 'rest'] choices=['cli', 'rest']
) )
} }
@ -94,6 +95,14 @@ def get_provider_argspec():
return f5_provider_spec return f5_provider_spec
def load_params(params):
provider = params.get('provider') or dict()
for key, value in iteritems(provider):
if key in f5_argument_spec:
if params.get(key) is None and value is not None:
params[key] = value
# Fully Qualified name (with the partition) # Fully Qualified name (with the partition)
def fqdn_name(partition, value): def fqdn_name(partition, value):
if value is not None and not value.startswith('/'): if value is not None and not value.startswith('/'):
@ -143,7 +152,8 @@ def cleanup_tokens(client):
def is_cli(module): def is_cli(module):
transport = module.params['transport'] transport = module.params['transport']
provider_transport = (module.params['provider'] or {}).get('transport') provider_transport = (module.params['provider'] or {}).get('transport')
return 'cli' in (transport, provider_transport) result = 'cli' in (transport, provider_transport)
return result
class Noop(object): class Noop(object):
@ -163,6 +173,7 @@ class Noop(object):
class F5BaseClient(object): class F5BaseClient(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.params = kwargs self.params = kwargs
load_params(self.params)
@property @property
def api(self): def api(self):

@ -163,7 +163,6 @@ import re
import time import time
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.module_utils.network.common.parsing import FailedConditionsError from ansible.module_utils.network.common.parsing import FailedConditionsError
from ansible.module_utils.network.common.parsing import Conditional from ansible.module_utils.network.common.parsing import Conditional
@ -259,7 +258,8 @@ class ModuleManager(object):
self.module = kwargs.get('module', None) self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None) self.client = kwargs.get('client', None)
self.want = Parameters(params=self.module.params) self.want = Parameters(params=self.module.params)
self.changes = Parameters() self.want.update({'module': self.module})
self.changes = Parameters(module=self.module)
def _to_lines(self, stdout): def _to_lines(self, stdout):
lines = list() lines = list()
@ -346,7 +346,7 @@ class ModuleManager(object):
'stdout_lines': self._to_lines(responses), 'stdout_lines': self._to_lines(responses),
'warnings': warnings 'warnings': warnings
} }
self.changes = Parameters(params=changes) self.changes = Parameters(params=changes, module=self.module)
if any(x for x in self.want.user_commands if x.startswith(changed)): if any(x for x in self.want.user_commands if x.startswith(changed)):
return True return True
return False return False

@ -287,10 +287,8 @@ class ModuleManager(object):
def remove_from_device(self): def remove_from_device(self):
result = self.client.api.tm.cm.remove_from_trust.exec_cmd( result = self.client.api.tm.cm.remove_from_trust.exec_cmd(
'run', deviceName=self.want.peer_hostname 'run', deviceName=self.want.peer_hostname, name=self.want.peer_hostname
) )
if result:
result.delete()
class ArgumentSpec(object): class ArgumentSpec(object):

@ -24,6 +24,7 @@ options:
description: description:
- The IP addresses for the new self IP. This value is ignored upon update - The IP addresses for the new self IP. This value is ignored upon update
as addresses themselves cannot be changed after they are created. as addresses themselves cannot be changed after they are created.
- This value is required when creating new self IPs.
allow_service: allow_service:
description: description:
- Configure port lockdown for the Self IP. By default, the Self IP has a - Configure port lockdown for the Self IP. By default, the Self IP has a
@ -62,6 +63,7 @@ options:
description: description:
- The route domain id of the system. When creating a new Self IP, if - The route domain id of the system. When creating a new Self IP, if
this value is not specified, a default value of C(0) will be used. this value is not specified, a default value of C(0) will be used.
- This value cannot be changed after it is set.
version_added: 2.3 version_added: 2.3
partition: partition:
description: description:
@ -249,7 +251,7 @@ except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
try: try:
from netaddr import IPNetwork, AddrFormatError, IPAddress import netaddr
HAS_NETADDR = True HAS_NETADDR = True
except ImportError: except ImportError:
HAS_NETADDR = False HAS_NETADDR = False
@ -262,11 +264,11 @@ class Parameters(AnsibleF5Parameters):
} }
updatables = [ updatables = [
'traffic_group', 'allow_service', 'vlan', 'route_domain', 'netmask' 'traffic_group', 'allow_service', 'vlan', 'netmask', 'address'
] ]
returnables = [ returnables = [
'traffic_group', 'allow_service', 'vlan', 'route_domain', 'netmask' 'traffic_group', 'allow_service', 'vlan', 'route_domain', 'netmask', 'address'
] ]
api_attributes = [ api_attributes = [
@ -280,6 +282,19 @@ class Parameters(AnsibleF5Parameters):
result = self._filter_params(result) result = self._filter_params(result)
return result return result
def _fqdn_name(self, value):
if value is not None and not value.startswith('/'):
return '/{0}/{1}'.format(self.partition, value)
return value
@property
def vlan(self):
if self._values['vlan'] is None:
return None
return self._fqdn_name(self._values['vlan'])
class ModuleParameters(Parameters):
@property @property
def address(self): def address(self):
address = "{0}%{1}/{2}".format( address = "{0}%{1}/{2}".format(
@ -287,18 +302,14 @@ class Parameters(AnsibleF5Parameters):
) )
return address return address
@address.setter
def address(self, value):
self._values['ip'] = value
@property @property
def ip(self): def ip(self):
if self._values['ip'] is None: if self._values['address'] is None:
return None return None
try: try:
ip = str(IPAddress(self._values['ip'])) ip = str(netaddr.IPAddress(self._values['address']))
return ip return ip
except AddrFormatError: except netaddr.AddrFormatError:
raise F5ModuleError( raise F5ModuleError(
'The provided address is not a valid IP address' 'The provided address is not a valid IP address'
) )
@ -333,13 +344,13 @@ class Parameters(AnsibleF5Parameters):
try: try:
# IPv4 netmask # IPv4 netmask
address = '0.0.0.0/' + self._values['netmask'] address = '0.0.0.0/' + self._values['netmask']
ip = IPNetwork(address) ip = netaddr.IPNetwork(address)
except AddrFormatError as ex: except netaddr.AddrFormatError as ex:
try: try:
# IPv6 netmask # IPv6 netmask
address = '::/' + self._values['netmask'] address = '::/' + self._values['netmask']
ip = IPNetwork(address) ip = netaddr.IPNetwork(address)
except AddrFormatError as ex: except netaddr.AddrFormatError as ex:
raise F5ModuleError( raise F5ModuleError(
'The provided netmask {0} is neither in IP or CIDR format'.format(self._values['netmask']) 'The provided netmask {0} is neither in IP or CIDR format'.format(self._values['netmask'])
) )
@ -405,70 +416,57 @@ class Parameters(AnsibleF5Parameters):
result = sorted(list(set(result))) result = sorted(list(set(result)))
return result return result
def _fqdn_name(self, value):
if value is not None and not value.startswith('/'):
return '/{0}/{1}'.format(self.partition, value)
return value
class ApiParameters(Parameters):
@property @property
def vlan(self): def allow_service(self):
if self._values['vlan'] is None: if self._values['allow_service'] is None:
return None return None
return self._fqdn_name(self._values['vlan']) if self._values['allow_service'] == 'all':
self._values['allow_service'] = ['all']
return sorted(self._values['allow_service'])
class ApiParameters(Parameters):
api_map = {}
@property @property
def address(self): def destination_ip(self):
if self.ip and self.route_domain and self.netmask: if self._values['address'] is None:
return '{0}%{1}/{2}'.format(self.ip, self.route_domain, self.netmask) return None
elif self.ip and self.netmask:
return '{0}/{1}'.format(self.ip, self.netmask)
@address.setter
def address(self, value):
pattern = r'^(?P<ip>[0-9A-Fa-f:.]+)%?(?P<rd>\d+)?\/(?P<nm>\d+)$'
matches = re.match(pattern, value)
if not matches:
raise F5ModuleError(
"The specified address is malformed. Please see documentation."
)
try: try:
ip = matches.group('ip') pattern = r'(?P<rd>%[0-9]+)'
self._values['ip'] = str(IPAddress(ip)) addr = re.sub(pattern, '', self._values['address'])
except AddrFormatError: ip = netaddr.IPNetwork(addr)
return '{0}/{1}'.format(ip.ip, ip.prefixlen)
except netaddr.AddrFormatError:
raise F5ModuleError( raise F5ModuleError(
'The provided address is not a valid IP address' "The provided destination is not an IP address"
) )
self._values['route_domain'] = matches.group('rd')
self._values['netmask'] = matches.group('nm')
@property @property
def allow_service(self): def netmask(self):
if self._values['allow_service'] is None: ip = netaddr.IPNetwork(self.destination_ip)
return None return int(ip.prefixlen)
return sorted(self._values['allow_service'])
@property @property
def trafficGroup(self): def ip(self):
return self.traffic_group result = netaddr.IPNetwork(self.destination_ip)
return str(result.ip)
@trafficGroup.setter
def trafficGroup(self, value):
self._values['traffic_group'] = value
class Changes(Parameters):
pass
class UsableChanges(Changes):
@property @property
def allowService(self): def allow_service(self):
return self._values['allow_service'] if self._values['allow_service'] is None:
return None
if self._values['allow_service'] == ['all']:
return 'all'
return sorted(self._values['allow_service'])
@allowService.setter
def allowService(self, value): class ReportableChanges(Changes):
if value == 'all': pass
self._values['allow_service'] = ['all']
else:
self._values['allow_service'] = sorted([str(x) for x in value])
class ModuleManager(object): class ModuleManager(object):
@ -476,8 +474,8 @@ class ModuleManager(object):
self.module = kwargs.get('module', None) self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None) self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = Parameters(params=self.module.params) self.want = ModuleParameters(params=self.module.params)
self.changes = ApiParameters() self.changes = UsableChanges()
def _set_changed_options(self): def _set_changed_options(self):
changed = {} changed = {}
@ -485,7 +483,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = Parameters(params=changed) self.changes = UsableChanges(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -496,12 +494,12 @@ class ModuleManager(object):
if change is None: if change is None:
continue continue
else: else:
if k in ['netmask', 'route_domain']: if k in ['netmask']:
changed['address'] = change changed['address'] = change
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = ApiParameters(params=changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
@ -583,11 +581,12 @@ class ModuleManager(object):
self.want.update({'route_domain': 0}) self.want.update({'route_domain': 0})
if self.want.allow_service: if self.want.allow_service:
if 'all' in self.want.allow_service: if 'all' in self.want.allow_service:
self.want.update(dict(allow_service='all')) self.want.update(dict(allow_service=['all']))
elif 'none' in self.want.allow_service: elif 'none' in self.want.allow_service:
self.want.update(dict(allow_service=[])) self.want.update(dict(allow_service=[]))
elif 'default' in self.want.allow_service: elif 'default' in self.want.allow_service:
self.want.update(dict(allow_service=['default'])) self.want.update(dict(allow_service=['default']))
self._set_changed_options()
if self.want.check_mode: if self.want.check_mode:
return True return True
self.create_on_device() self.create_on_device()
@ -597,7 +596,7 @@ class ModuleManager(object):
raise F5ModuleError("Failed to create the Self IP") raise F5ModuleError("Failed to create the Self IP")
def create_on_device(self): def create_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
self.client.api.tm.net.selfips.selfip.create( self.client.api.tm.net.selfips.selfip.create(
name=self.want.name, name=self.want.name,
partition=self.want.partition, partition=self.want.partition,
@ -648,6 +647,10 @@ class Difference(object):
except AttributeError: except AttributeError:
return attr1 return attr1
@property
def address(self):
pass
@property @property
def allow_service(self): def allow_service(self):
"""Returns services formatted for consumption by f5-sdk update """Returns services formatted for consumption by f5-sdk update
@ -670,7 +673,7 @@ class Difference(object):
if result[0] == 'none' and self.have.allow_service is None: if result[0] == 'none' and self.have.allow_service is None:
return None return None
elif result[0] == 'all' and self.have.allow_service[0] != 'all': elif result[0] == 'all' and self.have.allow_service[0] != 'all':
return 'all' return ['all']
elif result[0] == 'none': elif result[0] == 'none':
return [] return []
elif self.have.allow_service is None: elif self.have.allow_service is None:
@ -683,7 +686,7 @@ class Difference(object):
if self.want.netmask is None: if self.want.netmask is None:
return None return None
try: try:
address = IPNetwork(self.have.ip) address = netaddr.IPNetwork(self.have.ip)
if self.want.route_domain is not None: if self.want.route_domain is not None:
nipnet = "{0}%{1}/{2}".format(address.ip, self.want.route_domain, self.want.netmask) nipnet = "{0}%{1}/{2}".format(address.ip, self.want.route_domain, self.want.netmask)
cipnet = "{0}%{1}/{2}".format(address.ip, self.want.route_domain, self.have.netmask) cipnet = "{0}%{1}/{2}".format(address.ip, self.want.route_domain, self.have.netmask)
@ -695,32 +698,11 @@ class Difference(object):
cipnet = "{0}/{1}".format(address.ip, self.have.netmask) cipnet = "{0}/{1}".format(address.ip, self.have.netmask)
if nipnet != cipnet: if nipnet != cipnet:
return nipnet return nipnet
except AddrFormatError: except netaddr.AddrFormatError:
raise F5ModuleError( raise F5ModuleError(
'The provided address/netmask value "{0}" was invalid'.format(self.have.ip) 'The provided address/netmask value "{0}" was invalid'.format(self.have.ip)
) )
@property
def route_domain(self):
if self.want.route_domain is None:
return None
try:
address = IPNetwork(self.have.ip)
if self.want.netmask is not None:
nipnet = "{0}%{1}/{2}".format(address.ip, self.want.route_domain, self.want.netmask)
cipnet = "{0}%{1}/{2}".format(address.ip, self.have.route_domain, self.want.netmask)
elif self.have.netmask is not None:
nipnet = "{0}%{1}/{2}".format(address.ip, self.want.route_domain, self.have.netmask)
cipnet = "{0}%{1}/{2}".format(address.ip, self.have.route_domain, self.have.netmask)
if nipnet != cipnet:
return nipnet
except AddrFormatError:
raise F5ModuleError(
'The provided address/netmask value was invalid'
)
@property @property
def traffic_group(self): def traffic_group(self):
if self.want.traffic_group != self.have.traffic_group: if self.want.traffic_group != self.have.traffic_group:
@ -737,7 +719,7 @@ class ArgumentSpec(object):
netmask=dict(), netmask=dict(),
traffic_group=dict(), traffic_group=dict(),
vlan=dict(), vlan=dict(),
route_domain=dict(), route_domain=dict(type='int'),
state=dict( state=dict(
default='present', default='present',
choices=['present', 'absent'] choices=['present', 'absent']

@ -43,10 +43,6 @@ except ImportError:
class ActionModule(_ActionModule): class ActionModule(_ActionModule):
def run(self, tmp=None, task_vars=None): def run(self, tmp=None, task_vars=None):
transport = self._task.args.get('transport', 'rest')
display.vvvv('connection transport is %s' % transport, self._play_context.remote_addr)
if self._play_context.connection == 'network_cli': if self._play_context.connection == 'network_cli':
provider = self._task.args.get('provider', {}) provider = self._task.args.get('provider', {})
if any(provider.values()): if any(provider.values()):
@ -67,7 +63,7 @@ class ActionModule(_ActionModule):
pc.remote_user = provider.get('user', self._play_context.connection_user) pc.remote_user = provider.get('user', self._play_context.connection_user)
pc.password = provider.get('password', self._play_context.password) pc.password = provider.get('password', self._play_context.password)
pc.private_key_file = provider['ssh_keyfile'] or self._play_context.private_key_file pc.private_key_file = provider['ssh_keyfile'] or self._play_context.private_key_file
pc.timeout = int(provider.get('timeout', C.PERSISTENT_COMMAND_TIMEOUT)) pc.timeout = int(provider['timeout'] or C.PERSISTENT_COMMAND_TIMEOUT)
display.vvv('using connection plugin %s' % pc.connection, pc.remote_addr) display.vvv('using connection plugin %s' % pc.connection, pc.remote_addr)
connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin) connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin)

@ -21,8 +21,8 @@ from ansible.compat.tests.mock import patch
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_selfip import Parameters
from library.bigip_selfip import ApiParameters from library.bigip_selfip import ApiParameters
from library.bigip_selfip import ModuleParameters
from library.bigip_selfip import ModuleManager from library.bigip_selfip import ModuleManager
from library.bigip_selfip import ArgumentSpec from library.bigip_selfip import ArgumentSpec
from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import F5ModuleError
@ -30,8 +30,8 @@ try:
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
from ansible.modules.network.f5.bigip_selfip import Parameters
from ansible.modules.network.f5.bigip_selfip import ApiParameters from ansible.modules.network.f5.bigip_selfip import ApiParameters
from ansible.modules.network.f5.bigip_selfip import ModuleParameters
from ansible.modules.network.f5.bigip_selfip import ModuleManager from ansible.modules.network.f5.bigip_selfip import ModuleManager
from ansible.modules.network.f5.bigip_selfip import ArgumentSpec from ansible.modules.network.f5.bigip_selfip import ArgumentSpec
from ansible.module_utils.network.f5.common import F5ModuleError from ansible.module_utils.network.f5.common import F5ModuleError
@ -79,7 +79,7 @@ class TestParameters(unittest.TestCase):
traffic_group='traffic-group-local-only', traffic_group='traffic-group-local-only',
vlan='net1' vlan='net1'
) )
p = Parameters(params=args) p = ModuleParameters(params=args)
assert p.address == '10.10.10.10%1/24' assert p.address == '10.10.10.10%1/24'
assert p.allow_service == ['gre:0', 'tcp:80', 'udp:53'] assert p.allow_service == ['gre:0', 'tcp:80', 'udp:53']
assert p.name == 'net1' assert p.name == 'net1'
@ -96,7 +96,7 @@ class TestParameters(unittest.TestCase):
'grp' 'grp'
] ]
) )
p = Parameters(params=args) p = ModuleParameters(params=args)
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
assert p.allow_service == ['grp', 'tcp:80', 'udp:53'] assert p.allow_service == ['grp', 'tcp:80', 'udp:53']
assert 'The provided protocol' in str(ex) assert 'The provided protocol' in str(ex)
@ -119,7 +119,6 @@ class TestParameters(unittest.TestCase):
assert p.allow_service == ['gre', 'tcp:80', 'udp:53'] assert p.allow_service == ['gre', 'tcp:80', 'udp:53']
assert p.name == 'net1' assert p.name == 'net1'
assert p.netmask == 24 assert p.netmask == 24
assert p.route_domain == 1
assert p.traffic_group == '/Common/traffic-group-local-only' assert p.traffic_group == '/Common/traffic-group-local-only'
assert p.vlan == '/Common/net1' assert p.vlan == '/Common/net1'

Loading…
Cancel
Save