summaryrefslogtreecommitdiffstats
path: root/lib/ansible/module_utils/common/network.py
blob: c3874f89e1daeaf90f120ae934b153412dcfc375 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# Copyright (c) 2016 Red Hat Inc
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)

# General networking tools that may be used by all modules

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import re
from struct import pack
from socket import inet_ntoa

from ansible.module_utils.six.moves import zip


VALID_MASKS = [2**8 - 2**i for i in range(0, 9)]


def is_netmask(val):
    parts = str(val).split('.')
    if not len(parts) == 4:
        return False
    for part in parts:
        try:
            if int(part) not in VALID_MASKS:
                raise ValueError
        except ValueError:
            return False
    return True


def is_masklen(val):
    try:
        return 0 <= int(val) <= 32
    except ValueError:
        return False


def to_netmask(val):
    """ converts a masklen to a netmask """
    if not is_masklen(val):
        raise ValueError('invalid value for masklen')

    bits = 0
    for i in range(32 - int(val), 32):
        bits |= (1 << i)

    return inet_ntoa(pack('>I', bits))


def to_masklen(val):
    """ converts a netmask to a masklen """
    if not is_netmask(val):
        raise ValueError('invalid value for netmask: %s' % val)

    bits = list()
    for x in val.split('.'):
        octet = bin(int(x)).count('1')
        bits.append(octet)

    return sum(bits)


def to_subnet(addr, mask, dotted_notation=False):
    """ coverts an addr / mask pair to a subnet in cidr notation """
    try:
        if not is_masklen(mask):
            raise ValueError
        cidr = int(mask)
        mask = to_netmask(mask)
    except ValueError:
        cidr = to_masklen(mask)

    addr = addr.split('.')
    mask = mask.split('.')

    network = list()
    for s_addr, s_mask in zip(addr, mask):
        network.append(str(int(s_addr) & int(s_mask)))

    if dotted_notation:
        return '%s %s' % ('.'.join(network), to_netmask(cidr))
    return '%s/%s' % ('.'.join(network), cidr)


def to_ipv6_subnet(addr):
    """ IPv6 addresses are eight groupings. The first four groupings (64 bits) comprise the subnet address. """

    # https://tools.ietf.org/rfc/rfc2374.txt

    # Split by :: to identify omitted zeros
    ipv6_prefix = addr.split('::')[0]

    # Get the first four groups, or as many as are found + ::
    found_groups = []
    for group in ipv6_prefix.split(':'):
        found_groups.append(group)
        if len(found_groups) == 4:
            break
    if len(found_groups) < 4:
        found_groups.append('::')

    # Concatenate network address parts
    network_addr = ''
    for group in found_groups:
        if group != '::':
            network_addr += str(group)
        network_addr += str(':')

    # Ensure network address ends with ::
    if not network_addr.endswith('::'):
        network_addr += str(':')
    return network_addr


def to_ipv6_network(addr):
    """ IPv6 addresses are eight groupings. The first three groupings (48 bits) comprise the network address. """

    # Split by :: to identify omitted zeros
    ipv6_prefix = addr.split('::')[0]

    # Get the first three groups, or as many as are found + ::
    found_groups = []
    for group in ipv6_prefix.split(':'):
        found_groups.append(group)
        if len(found_groups) == 3:
            break
    if len(found_groups) < 3:
        found_groups.append('::')

    # Concatenate network address parts
    network_addr = ''
    for group in found_groups:
        if group != '::':
            network_addr += str(group)
        network_addr += str(':')

    # Ensure network address ends with ::
    if not network_addr.endswith('::'):
        network_addr += str(':')
    return network_addr


def to_bits(val):
    """ converts a netmask to bits """
    bits = ''
    for octet in val.split('.'):
        bits += bin(int(octet))[2:].zfill(8)
    return bits


def is_mac(mac_address):
    """
    Validate MAC address for given string
    Args:
        mac_address: string to validate as MAC address

    Returns: (Boolean) True if string is valid MAC address, otherwise False
    """
    mac_addr_regex = re.compile('[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$')
    return bool(mac_addr_regex.match(mac_address.lower()))