From 9a17ee6d8433ad442b6de3053cf4e9b889c47f63 Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Wed, 22 Aug 2018 15:16:55 -0400 Subject: [PATCH] Removes netaddr dependency (#44519) One more dep removed from bigip_selfip --- .../modules/network/f5/bigip_selfip.py | 246 +++++++++--------- 1 file changed, 122 insertions(+), 124 deletions(-) diff --git a/lib/ansible/modules/network/f5/bigip_selfip.py b/lib/ansible/modules/network/f5/bigip_selfip.py index 045d57780b..65bcb4f088 100644 --- a/lib/ansible/modules/network/f5/bigip_selfip.py +++ b/lib/ansible/modules/network/f5/bigip_selfip.py @@ -74,11 +74,7 @@ options: other resources on a BIG-IP are. default: Common version_added: 2.5 -notes: - - Requires the netaddr Python package on the host. extends_documentation_fragment: f5 -requirements: - - netaddr author: - Tim Rupp (@caphrim007) ''' @@ -229,6 +225,11 @@ 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 + from library.module_utils.network.f5.ipaddress import ipv6_netmask_to_cidr + from library.module_utils.compat.ipaddress import ip_address + from library.module_utils.compat.ipaddress import ip_network + from library.module_utils.compat.ipaddress import ip_interface try: from library.module_utils.network.f5.common import iControlUnexpectedHTTPError except ImportError: @@ -241,17 +242,16 @@ 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 + from ansible.module_utils.network.f5.ipaddress import ipv6_netmask_to_cidr + from ansible.module_utils.compat.ipaddress import ip_address + from ansible.module_utils.compat.ipaddress import ip_network + from ansible.module_utils.compat.ipaddress import ip_interface 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 = { @@ -297,10 +297,9 @@ class ModuleParameters(Parameters): def ip(self): if self._values['address'] is None: return None - try: - ip = str(netaddr.IPAddress(self._values['address'])) - return ip - except netaddr.AddrFormatError: + if is_valid_ip(self._values['address']): + return self._values['address'] + else: raise F5ModuleError( 'The provided address is not a valid IP address' ) @@ -322,30 +321,23 @@ class ModuleParameters(Parameters): def netmask(self): if self._values['netmask'] is None: return None - - # Check if numeric - if isinstance(self._values['netmask'], int): + result = -1 + try: result = int(self._values['netmask']) if 0 < result < 256: - return result + pass + except ValueError: + if is_valid_ip(self._values['netmask']): + addr = ip_address(u'{0}'.format(str(self._values['netmask']))) + if addr.version == 4: + ip = ip_network(u'0.0.0.0/%s' % str(self._values['netmask'])) + result = ip.prefixlen + else: + result = ipv6_netmask_to_cidr(self._values['netmask']) + if result < 0: raise F5ModuleError( 'The provided netmask {0} is neither in IP or CIDR format'.format(result) ) - else: - try: - # IPv4 netmask - address = '0.0.0.0/' + self._values['netmask'] - ip = netaddr.IPNetwork(address) - except netaddr.AddrFormatError as ex: - try: - # IPv6 netmask - address = '::/' + self._values['netmask'] - ip = netaddr.IPNetwork(address) - except netaddr.AddrFormatError as ex: - raise F5ModuleError( - 'The provided netmask {0} is neither in IP or CIDR format'.format(self._values['netmask']) - ) - result = int(ip.prefixlen) return result @property @@ -424,21 +416,21 @@ class ApiParameters(Parameters): try: pattern = r'(?P%[0-9]+)' addr = re.sub(pattern, '', self._values['address']) - ip = netaddr.IPNetwork(addr) - return '{0}/{1}'.format(ip.ip, ip.prefixlen) - except netaddr.AddrFormatError: + ip = ip_interface(u'{0}'.format(addr)) + return ip.with_prefixlen + except ValueError: raise F5ModuleError( "The provided destination is not an IP address" ) @property def netmask(self): - ip = netaddr.IPNetwork(self.destination_ip) - return int(ip.prefixlen) + ip = ip_interface(self.destination_ip) + return int(ip.network.prefixlen) @property def ip(self): - result = netaddr.IPNetwork(self.destination_ip) + result = ip_interface(self.destination_ip) return str(result.ip) @@ -446,6 +438,89 @@ class Changes(Parameters): pass +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 address(self): + return None + + @property + def allow_service(self): + """Returns services formatted for consumption by f5-sdk update + + The BIG-IP endpoint for services takes different values depending on + what you want the "allowed services" to be. It can be any of the + following + + - a list containing "protocol:port" values + - the string "all" + - a null value, or None + + This is a convenience function to massage the values the user has + supplied so that they are formatted in such a way that BIG-IP will + accept them and apply the specified policy. + """ + if self.want.allow_service is None: + return None + result = self.want.allow_service + if result[0] == 'none' and self.have.allow_service is None: + return None + elif self.have.allow_service is None: + return result + elif result[0] == 'all' and self.have.allow_service[0] != 'all': + return ['all'] + elif result[0] == 'none': + return [] + elif set(self.want.allow_service) != set(self.have.allow_service): + return result + + @property + def netmask(self): + if self.want.netmask is None: + return None + ip = self.have.ip + if is_valid_ip(ip): + if self.want.route_domain is not None: + want = "{0}%{1}/{2}".format(ip, self.want.route_domain, self.want.netmask) + have = "{0}%{1}/{2}".format(ip, self.want.route_domain, self.have.netmask) + elif self.have.route_domain is not None: + want = "{0}%{1}/{2}".format(ip, self.have.route_domain, self.want.netmask) + have = "{0}%{1}/{2}".format(ip, self.have.route_domain, self.have.netmask) + else: + want = "{0}/{1}".format(ip, self.want.netmask) + have = "{0}/{1}".format(ip, self.have.netmask) + if want != have: + return want + else: + raise F5ModuleError( + 'The provided address/netmask value "{0}" was invalid'.format(self.have.ip) + ) + + @property + def traffic_group(self): + if self.want.traffic_group != self.have.traffic_group: + return self.want.traffic_group + + class UsableChanges(Changes): @property def allow_service(self): @@ -557,6 +632,10 @@ class ModuleManager(object): ) resource.modify(**params) + def read_partition_default_route_domain_from_device(self): + resource = self.client.api.tm.auth.partitions.partition.load(name=self.want.partition) + return int(resource.defaultRouteDomain) + def create(self): if self.want.address is None or self.want.netmask is None: raise F5ModuleError( @@ -566,6 +645,10 @@ class ModuleManager(object): raise F5ModuleError( 'A VLAN name must be specified' ) + if self.want.route_domain is None: + rd = self.read_partition_default_route_domain_from_device() + self.want.update({'route_domain': rd}) + if self.want.traffic_group is None: self.want.update({'traffic_group': '/Common/traffic-group-local-only'}) if self.want.route_domain is None: @@ -617,89 +700,6 @@ class ModuleManager(object): return result -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 address(self): - pass - - @property - def allow_service(self): - """Returns services formatted for consumption by f5-sdk update - - The BIG-IP endpoint for services takes different values depending on - what you want the "allowed services" to be. It can be any of the - following - - - a list containing "protocol:port" values - - the string "all" - - a null value, or None - - This is a convenience function to massage the values the user has - supplied so that they are formatted in such a way that BIG-IP will - accept them and apply the specified policy. - """ - if self.want.allow_service is None: - return None - result = self.want.allow_service - if result[0] == 'none' and self.have.allow_service is None: - return None - elif result[0] == 'all' and self.have.allow_service[0] != 'all': - return ['all'] - elif result[0] == 'none': - return [] - elif self.have.allow_service is None: - return result - elif set(self.want.allow_service) != set(self.have.allow_service): - return result - - @property - def netmask(self): - if self.want.netmask is None: - return None - try: - address = netaddr.IPNetwork(self.have.ip) - if self.want.route_domain 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.want.route_domain, self.have.netmask) - elif self.have.route_domain is not None: - nipnet = "{0}%{1}/{2}".format(address.ip, self.have.route_domain, self.want.netmask) - cipnet = "{0}%{1}/{2}".format(address.ip, self.have.route_domain, self.have.netmask) - else: - nipnet = "{0}/{1}".format(address.ip, self.want.netmask) - cipnet = "{0}/{1}".format(address.ip, self.have.netmask) - if nipnet != cipnet: - return nipnet - except netaddr.AddrFormatError: - raise F5ModuleError( - 'The provided address/netmask value "{0}" was invalid'.format(self.have.ip) - ) - - @property - def traffic_group(self): - if self.want.traffic_group != self.have.traffic_group: - return self.want.traffic_group - - class ArgumentSpec(object): def __init__(self): self.supports_check_mode = True @@ -734,8 +734,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)