~corey.bryant/charms/trusty/neutron-api/db-stamp

« back to all changes in this revision

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

  • Committer: james.page at ubuntu
  • Date: 2014-10-01 21:05:24 UTC
  • mfrom: (39.3.25 ipv6)
  • Revision ID: james.page@ubuntu.com-20141001210524-z6uqyljzorphrhy6
[xianghui,dosaboy,r=james-page,t=gema] Add IPv6 support using prefer-ipv6 flag

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import glob
 
2
import re
 
3
import subprocess
1
4
import sys
2
5
 
3
6
from functools import partial
4
7
 
 
8
from charmhelpers.core.hookenv import unit_get
5
9
from charmhelpers.fetch import apt_install
6
10
from charmhelpers.core.hookenv import (
7
 
    ERROR, log,
 
11
    WARNING,
 
12
    ERROR,
 
13
    log
8
14
)
9
15
 
10
16
try:
154
160
get_iface_for_address = partial(_get_for_address, key='iface')
155
161
 
156
162
get_netmask_for_address = partial(_get_for_address, key='netmask')
 
163
 
 
164
 
 
165
def format_ipv6_addr(address):
 
166
    """
 
167
    IPv6 needs to be wrapped with [] in url link to parse correctly.
 
168
    """
 
169
    if is_ipv6(address):
 
170
        address = "[%s]" % address
 
171
    else:
 
172
        log("Not a valid ipv6 address: %s" % address, level=WARNING)
 
173
        address = None
 
174
 
 
175
    return address
 
176
 
 
177
 
 
178
def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False,
 
179
                   fatal=True, exc_list=None):
 
180
    """
 
181
    Return the assigned IP address for a given interface, if any, or [].
 
182
    """
 
183
    # Extract nic if passed /dev/ethX
 
184
    if '/' in iface:
 
185
        iface = iface.split('/')[-1]
 
186
    if not exc_list:
 
187
        exc_list = []
 
188
    try:
 
189
        inet_num = getattr(netifaces, inet_type)
 
190
    except AttributeError:
 
191
        raise Exception('Unknown inet type ' + str(inet_type))
 
192
 
 
193
    interfaces = netifaces.interfaces()
 
194
    if inc_aliases:
 
195
        ifaces = []
 
196
        for _iface in interfaces:
 
197
            if iface == _iface or _iface.split(':')[0] == iface:
 
198
                ifaces.append(_iface)
 
199
        if fatal and not ifaces:
 
200
            raise Exception("Invalid interface '%s'" % iface)
 
201
        ifaces.sort()
 
202
    else:
 
203
        if iface not in interfaces:
 
204
            if fatal:
 
205
                raise Exception("%s not found " % (iface))
 
206
            else:
 
207
                return []
 
208
        else:
 
209
            ifaces = [iface]
 
210
 
 
211
    addresses = []
 
212
    for netiface in ifaces:
 
213
        net_info = netifaces.ifaddresses(netiface)
 
214
        if inet_num in net_info:
 
215
            for entry in net_info[inet_num]:
 
216
                if 'addr' in entry and entry['addr'] not in exc_list:
 
217
                    addresses.append(entry['addr'])
 
218
    if fatal and not addresses:
 
219
        raise Exception("Interface '%s' doesn't have any %s addresses." %
 
220
                        (iface, inet_type))
 
221
    return addresses
 
222
 
 
223
get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET')
 
224
 
 
225
 
 
226
def get_iface_from_addr(addr):
 
227
    """Work out on which interface the provided address is configured."""
 
228
    for iface in netifaces.interfaces():
 
229
        addresses = netifaces.ifaddresses(iface)
 
230
        for inet_type in addresses:
 
231
            for _addr in addresses[inet_type]:
 
232
                _addr = _addr['addr']
 
233
                # link local
 
234
                ll_key = re.compile("(.+)%.*")
 
235
                raw = re.match(ll_key, _addr)
 
236
                if raw:
 
237
                    _addr = raw.group(1)
 
238
                if _addr == addr:
 
239
                    log("Address '%s' is configured on iface '%s'" %
 
240
                        (addr, iface))
 
241
                    return iface
 
242
 
 
243
    msg = "Unable to infer net iface on which '%s' is configured" % (addr)
 
244
    raise Exception(msg)
 
245
 
 
246
 
 
247
def sniff_iface(f):
 
248
    """If no iface provided, inject net iface inferred from unit private
 
249
    address.
 
250
    """
 
251
    def iface_sniffer(*args, **kwargs):
 
252
        if not kwargs.get('iface', None):
 
253
            kwargs['iface'] = get_iface_from_addr(unit_get('private-address'))
 
254
 
 
255
        return f(*args, **kwargs)
 
256
 
 
257
    return iface_sniffer
 
258
 
 
259
 
 
260
@sniff_iface
 
261
def get_ipv6_addr(iface=None, inc_aliases=False, fatal=True, exc_list=None,
 
262
                  dynamic_only=True):
 
263
    """Get assigned IPv6 address for a given interface.
 
264
 
 
265
    Returns list of addresses found. If no address found, returns empty list.
 
266
 
 
267
    If iface is None, we infer the current primary interface by doing a reverse
 
268
    lookup on the unit private-address.
 
269
 
 
270
    We currently only support scope global IPv6 addresses i.e. non-temporary
 
271
    addresses. If no global IPv6 address is found, return the first one found
 
272
    in the ipv6 address list.
 
273
    """
 
274
    addresses = get_iface_addr(iface=iface, inet_type='AF_INET6',
 
275
                               inc_aliases=inc_aliases, fatal=fatal,
 
276
                               exc_list=exc_list)
 
277
 
 
278
    if addresses:
 
279
        global_addrs = []
 
280
        for addr in addresses:
 
281
            key_scope_link_local = re.compile("^fe80::..(.+)%(.+)")
 
282
            m = re.match(key_scope_link_local, addr)
 
283
            if m:
 
284
                eui_64_mac = m.group(1)
 
285
                iface = m.group(2)
 
286
            else:
 
287
                global_addrs.append(addr)
 
288
 
 
289
        if global_addrs:
 
290
            # Make sure any found global addresses are not temporary
 
291
            cmd = ['ip', 'addr', 'show', iface]
 
292
            out = subprocess.check_output(cmd)
 
293
            if dynamic_only:
 
294
                key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*")
 
295
            else:
 
296
                key = re.compile("inet6 (.+)/[0-9]+ scope global.*")
 
297
 
 
298
            addrs = []
 
299
            for line in out.split('\n'):
 
300
                line = line.strip()
 
301
                m = re.match(key, line)
 
302
                if m and 'temporary' not in line:
 
303
                    # Return the first valid address we find
 
304
                    for addr in global_addrs:
 
305
                        if m.group(1) == addr:
 
306
                            if not dynamic_only or \
 
307
                                    m.group(1).endswith(eui_64_mac):
 
308
                                addrs.append(addr)
 
309
 
 
310
            if addrs:
 
311
                return addrs
 
312
 
 
313
    if fatal:
 
314
        raise Exception("Interface '%s' doesn't have a scope global "
 
315
                        "non-temporary ipv6 address." % iface)
 
316
 
 
317
    return []
 
318
 
 
319
 
 
320
def get_bridges(vnic_dir='/sys/devices/virtual/net'):
 
321
    """
 
322
    Return a list of bridges on the system or []
 
323
    """
 
324
    b_rgex = vnic_dir + '/*/bridge'
 
325
    return [x.replace(vnic_dir, '').split('/')[1] for x in glob.glob(b_rgex)]
 
326
 
 
327
 
 
328
def get_bridge_nics(bridge, vnic_dir='/sys/devices/virtual/net'):
 
329
    """
 
330
    Return a list of nics comprising a given bridge on the system or []
 
331
    """
 
332
    brif_rgex = "%s/%s/brif/*" % (vnic_dir, bridge)
 
333
    return [x.split('/')[-1] for x in glob.glob(brif_rgex)]
 
334
 
 
335
 
 
336
def is_bridge_member(nic):
 
337
    """
 
338
    Check if a given nic is a member of a bridge
 
339
    """
 
340
    for bridge in get_bridges():
 
341
        if nic in get_bridge_nics(bridge):
 
342
            return True
 
343
    return False