~bjornt/charms/trusty/swift-storage/forward-port-bug-1350049-fix

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/network/ip.py

  • Committer: james.page at ubuntu
  • Date: 2014-07-28 12:08:48 UTC
  • Revision ID: james.page@ubuntu.com-20140728120848-6innvhyueo8evdmk
[trivial] Add missing helper from resync

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import sys
 
2
 
 
3
from functools import partial
 
4
 
 
5
from charmhelpers.fetch import apt_install
 
6
from charmhelpers.core.hookenv import (
 
7
    ERROR, log,
 
8
)
 
9
 
 
10
try:
 
11
    import netifaces
 
12
except ImportError:
 
13
    apt_install('python-netifaces')
 
14
    import netifaces
 
15
 
 
16
try:
 
17
    import netaddr
 
18
except ImportError:
 
19
    apt_install('python-netaddr')
 
20
    import netaddr
 
21
 
 
22
 
 
23
def _validate_cidr(network):
 
24
    try:
 
25
        netaddr.IPNetwork(network)
 
26
    except (netaddr.core.AddrFormatError, ValueError):
 
27
        raise ValueError("Network (%s) is not in CIDR presentation format" %
 
28
                         network)
 
29
 
 
30
 
 
31
def get_address_in_network(network, fallback=None, fatal=False):
 
32
    """
 
33
    Get an IPv4 or IPv6 address within the network from the host.
 
34
 
 
35
    :param network (str): CIDR presentation format. For example,
 
36
        '192.168.1.0/24'.
 
37
    :param fallback (str): If no address is found, return fallback.
 
38
    :param fatal (boolean): If no address is found, fallback is not
 
39
        set and fatal is True then exit(1).
 
40
 
 
41
    """
 
42
 
 
43
    def not_found_error_out():
 
44
        log("No IP address found in network: %s" % network,
 
45
            level=ERROR)
 
46
        sys.exit(1)
 
47
 
 
48
    if network is None:
 
49
        if fallback is not None:
 
50
            return fallback
 
51
        else:
 
52
            if fatal:
 
53
                not_found_error_out()
 
54
 
 
55
    _validate_cidr(network)
 
56
    network = netaddr.IPNetwork(network)
 
57
    for iface in netifaces.interfaces():
 
58
        addresses = netifaces.ifaddresses(iface)
 
59
        if network.version == 4 and netifaces.AF_INET in addresses:
 
60
            addr = addresses[netifaces.AF_INET][0]['addr']
 
61
            netmask = addresses[netifaces.AF_INET][0]['netmask']
 
62
            cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
 
63
            if cidr in network:
 
64
                return str(cidr.ip)
 
65
        if network.version == 6 and netifaces.AF_INET6 in addresses:
 
66
            for addr in addresses[netifaces.AF_INET6]:
 
67
                if not addr['addr'].startswith('fe80'):
 
68
                    cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
 
69
                                                        addr['netmask']))
 
70
                    if cidr in network:
 
71
                        return str(cidr.ip)
 
72
 
 
73
    if fallback is not None:
 
74
        return fallback
 
75
 
 
76
    if fatal:
 
77
        not_found_error_out()
 
78
 
 
79
    return None
 
80
 
 
81
 
 
82
def is_ipv6(address):
 
83
    '''Determine whether provided address is IPv6 or not'''
 
84
    try:
 
85
        address = netaddr.IPAddress(address)
 
86
    except netaddr.AddrFormatError:
 
87
        # probably a hostname - so not an address at all!
 
88
        return False
 
89
    else:
 
90
        return address.version == 6
 
91
 
 
92
 
 
93
def is_address_in_network(network, address):
 
94
    """
 
95
    Determine whether the provided address is within a network range.
 
96
 
 
97
    :param network (str): CIDR presentation format. For example,
 
98
        '192.168.1.0/24'.
 
99
    :param address: An individual IPv4 or IPv6 address without a net
 
100
        mask or subnet prefix. For example, '192.168.1.1'.
 
101
    :returns boolean: Flag indicating whether address is in network.
 
102
    """
 
103
    try:
 
104
        network = netaddr.IPNetwork(network)
 
105
    except (netaddr.core.AddrFormatError, ValueError):
 
106
        raise ValueError("Network (%s) is not in CIDR presentation format" %
 
107
                         network)
 
108
    try:
 
109
        address = netaddr.IPAddress(address)
 
110
    except (netaddr.core.AddrFormatError, ValueError):
 
111
        raise ValueError("Address (%s) is not in correct presentation format" %
 
112
                         address)
 
113
    if address in network:
 
114
        return True
 
115
    else:
 
116
        return False
 
117
 
 
118
 
 
119
def _get_for_address(address, key):
 
120
    """Retrieve an attribute of or the physical interface that
 
121
    the IP address provided could be bound to.
 
122
 
 
123
    :param address (str): An individual IPv4 or IPv6 address without a net
 
124
        mask or subnet prefix. For example, '192.168.1.1'.
 
125
    :param key: 'iface' for the physical interface name or an attribute
 
126
        of the configured interface, for example 'netmask'.
 
127
    :returns str: Requested attribute or None if address is not bindable.
 
128
    """
 
129
    address = netaddr.IPAddress(address)
 
130
    for iface in netifaces.interfaces():
 
131
        addresses = netifaces.ifaddresses(iface)
 
132
        if address.version == 4 and netifaces.AF_INET in addresses:
 
133
            addr = addresses[netifaces.AF_INET][0]['addr']
 
134
            netmask = addresses[netifaces.AF_INET][0]['netmask']
 
135
            cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
 
136
            if address in cidr:
 
137
                if key == 'iface':
 
138
                    return iface
 
139
                else:
 
140
                    return addresses[netifaces.AF_INET][0][key]
 
141
        if address.version == 6 and netifaces.AF_INET6 in addresses:
 
142
            for addr in addresses[netifaces.AF_INET6]:
 
143
                if not addr['addr'].startswith('fe80'):
 
144
                    cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
 
145
                                                        addr['netmask']))
 
146
                    if address in cidr:
 
147
                        if key == 'iface':
 
148
                            return iface
 
149
                        else:
 
150
                            return addr[key]
 
151
    return None
 
152
 
 
153
 
 
154
get_iface_for_address = partial(_get_for_address, key='iface')
 
155
 
 
156
get_netmask_for_address = partial(_get_for_address, key='netmask')